@docusaurus/plugin-content-docs 0.0.0-5262 → 0.0.0-5265

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.
package/lib/index.js CHANGED
@@ -19,10 +19,7 @@ const versions_1 = require("./versions");
19
19
  const cli_1 = require("./cli");
20
20
  const constants_1 = require("./constants");
21
21
  const globalData_1 = require("./globalData");
22
- const props_1 = require("./props");
23
- const categoryGeneratedIndex_1 = require("./categoryGeneratedIndex");
24
22
  const translations_1 = require("./translations");
25
- const tags_1 = require("./tags");
26
23
  const routes_1 = require("./routes");
27
24
  const utils_2 = require("./sidebars/utils");
28
25
  async function pluginContentDocs(context, options) {
@@ -125,77 +122,18 @@ async function pluginContentDocs(context, options) {
125
122
  return (0, translations_1.translateLoadedContent)(content, translationFiles);
126
123
  },
127
124
  async contentLoaded({ content, actions }) {
128
- const { loadedVersions } = content;
129
- const { docLayoutComponent, docItemComponent, docCategoryGeneratedIndexComponent, breadcrumbs, } = options;
130
- const { addRoute, createData, setGlobalData } = actions;
131
- const versions = loadedVersions.map((version) => {
132
- const sidebarsUtils = (0, utils_2.createSidebarsUtils)(version.sidebars);
133
- return {
134
- ...version,
135
- sidebarsUtils,
136
- categoryGeneratedIndices: (0, categoryGeneratedIndex_1.getCategoryGeneratedIndexMetadataList)({
137
- docs: version.docs,
138
- sidebarsUtils,
139
- }),
140
- };
141
- });
142
- async function createVersionTagsRoutes(version) {
143
- const versionTags = (0, tags_1.getVersionTags)(version.docs);
144
- // TODO tags should be a sub route of the version route
145
- async function createTagsListPage() {
146
- const tagsProp = Object.values(versionTags).map((tagValue) => ({
147
- label: tagValue.label,
148
- permalink: tagValue.permalink,
149
- count: tagValue.docIds.length,
150
- }));
151
- // Only create /tags page if there are tags.
152
- if (tagsProp.length > 0) {
153
- const tagsPropPath = await createData(`${(0, utils_1.docuHash)(`tags-list-${version.versionName}-prop`)}.json`, JSON.stringify(tagsProp, null, 2));
154
- addRoute({
155
- path: version.tagsPath,
156
- exact: true,
157
- component: options.docTagsListComponent,
158
- modules: {
159
- tags: aliasedSource(tagsPropPath),
160
- },
161
- });
162
- }
163
- }
164
- // TODO tags should be a sub route of the version route
165
- async function createTagDocListPage(tag) {
166
- const tagProps = (0, props_1.toTagDocListProp)({
167
- allTagsPath: version.tagsPath,
168
- tag,
169
- docs: version.docs,
170
- });
171
- const tagPropPath = await createData(`${(0, utils_1.docuHash)(`tag-${tag.permalink}`)}.json`, JSON.stringify(tagProps, null, 2));
172
- addRoute({
173
- path: tag.permalink,
174
- component: options.docTagDocListComponent,
175
- exact: true,
176
- modules: {
177
- tag: aliasedSource(tagPropPath),
178
- },
179
- });
180
- }
181
- await createTagsListPage();
182
- await Promise.all(Object.values(versionTags).map(createTagDocListPage));
183
- }
184
- await Promise.all(versions.map((version) => (0, routes_1.createVersionRoutes)({
185
- version,
186
- docItemComponent,
187
- docLayoutComponent,
188
- docCategoryGeneratedIndexComponent,
189
- pluginId,
190
- aliasedSource,
125
+ const versions = content.loadedVersions.map(versions_1.toFullVersion);
126
+ await (0, routes_1.createAllRoutes)({
127
+ baseUrl,
128
+ versions,
129
+ options,
191
130
  actions,
192
- })));
193
- // TODO tags should be a sub route of the version route
194
- await Promise.all(versions.map(createVersionTagsRoutes));
195
- setGlobalData({
131
+ aliasedSource,
132
+ });
133
+ actions.setGlobalData({
196
134
  path: (0, utils_1.normalizeUrl)([baseUrl, options.routeBasePath]),
197
135
  versions: versions.map(globalData_1.toGlobalDataVersion),
198
- breadcrumbs,
136
+ breadcrumbs: options.breadcrumbs,
199
137
  });
200
138
  },
201
139
  configureWebpack(_config, isServer, utils, content) {
package/lib/options.js CHANGED
@@ -21,7 +21,9 @@ exports.DEFAULT_OPTIONS = {
21
21
  exclude: utils_1.GlobExcludeDefault,
22
22
  sidebarItemsGenerator: generator_1.DefaultSidebarItemsGenerator,
23
23
  numberPrefixParser: numberPrefix_1.DefaultNumberPrefixParser,
24
- docLayoutComponent: '@theme/DocPage',
24
+ docsRootComponent: '@theme/DocsRoot',
25
+ docVersionRootComponent: '@theme/DocVersionRoot',
26
+ docRootComponent: '@theme/DocRoot',
25
27
  docItemComponent: '@theme/DocItem',
26
28
  docTagDocListComponent: '@theme/DocTagDocListPage',
27
29
  docTagsListComponent: '@theme/DocTagsListPage',
@@ -49,6 +51,7 @@ const VersionOptionsSchema = utils_validation_1.Joi.object({
49
51
  banner: utils_validation_1.Joi.string().equal('none', 'unreleased', 'unmaintained').optional(),
50
52
  badge: utils_validation_1.Joi.boolean().optional(),
51
53
  className: utils_validation_1.Joi.string().optional(),
54
+ noIndex: utils_validation_1.Joi.boolean().optional(),
52
55
  });
53
56
  const VersionsOptionsSchema = utils_validation_1.Joi.object()
54
57
  .pattern(utils_validation_1.Joi.string().required(), VersionOptionsSchema)
@@ -80,7 +83,9 @@ const OptionsSchema = utils_validation_1.Joi.object({
80
83
  then: utils_validation_1.Joi.custom((val) => val ? numberPrefix_1.DefaultNumberPrefixParser : numberPrefix_1.DisabledNumberPrefixParser),
81
84
  }))
82
85
  .default(() => exports.DEFAULT_OPTIONS.numberPrefixParser),
83
- docLayoutComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docLayoutComponent),
86
+ docsRootComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docsRootComponent),
87
+ docVersionRootComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docVersionRootComponent),
88
+ docRootComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docRootComponent),
84
89
  docItemComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docItemComponent),
85
90
  docTagsListComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docTagsListComponent),
86
91
  docTagDocListComponent: utils_validation_1.Joi.string().default(exports.DEFAULT_OPTIONS.docTagDocListComponent),
package/lib/props.d.ts CHANGED
@@ -4,8 +4,8 @@
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 type { VersionTag } from './types';
8
- import type { PropSidebars, PropVersionMetadata, PropTagDocList, DocMetadata, LoadedVersion } from '@docusaurus/plugin-content-docs';
7
+ import type { VersionTag, VersionTags } from './types';
8
+ import type { PropSidebars, PropVersionMetadata, PropTagDocList, PropTagsListPage, DocMetadata, LoadedVersion } from '@docusaurus/plugin-content-docs';
9
9
  export declare function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars;
10
10
  export declare function toVersionMetadataProp(pluginId: string, loadedVersion: LoadedVersion): PropVersionMetadata;
11
11
  export declare function toTagDocListProp({ allTagsPath, tag, docs, }: {
@@ -13,3 +13,4 @@ export declare function toTagDocListProp({ allTagsPath, tag, docs, }: {
13
13
  tag: VersionTag;
14
14
  docs: DocMetadata[];
15
15
  }): PropTagDocList;
16
+ export declare function toTagsListTagsProp(versionTags: VersionTags): PropTagsListPage['tags'];
package/lib/props.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.toTagDocListProp = exports.toVersionMetadataProp = exports.toSidebarsProp = void 0;
9
+ exports.toTagsListTagsProp = exports.toTagDocListProp = exports.toVersionMetadataProp = exports.toSidebarsProp = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
12
12
  const docs_1 = require("./docs");
@@ -98,6 +98,7 @@ function toVersionMetadataProp(pluginId, loadedVersion) {
98
98
  label: loadedVersion.label,
99
99
  banner: loadedVersion.banner,
100
100
  badge: loadedVersion.badge,
101
+ noIndex: loadedVersion.noIndex,
101
102
  className: loadedVersion.className,
102
103
  isLast: loadedVersion.isLast,
103
104
  docsSidebars: toSidebarsProp(loadedVersion),
@@ -126,3 +127,11 @@ function toTagDocListProp({ allTagsPath, tag, docs, }) {
126
127
  };
127
128
  }
128
129
  exports.toTagDocListProp = toTagDocListProp;
130
+ function toTagsListTagsProp(versionTags) {
131
+ return Object.values(versionTags).map((tagValue) => ({
132
+ label: tagValue.label,
133
+ permalink: tagValue.permalink,
134
+ count: tagValue.docIds.length,
135
+ }));
136
+ }
137
+ exports.toTagsListTagsProp = toTagsListTagsProp;
package/lib/routes.d.ts CHANGED
@@ -6,24 +6,17 @@
6
6
  */
7
7
  import type { PluginContentLoadedActions, RouteConfig } from '@docusaurus/types';
8
8
  import type { FullVersion } from './types';
9
- import type { DocMetadata } from '@docusaurus/plugin-content-docs';
10
- export declare function createCategoryGeneratedIndexRoutes({ version, actions, docCategoryGeneratedIndexComponent, aliasedSource, }: {
11
- version: FullVersion;
9
+ import type { PluginOptions } from '@docusaurus/plugin-content-docs';
10
+ declare type BuildAllRoutesParam = Omit<CreateAllRoutesParam, 'actions'> & {
11
+ actions: Omit<PluginContentLoadedActions, 'addRoute' | 'setGlobalData'>;
12
+ };
13
+ export declare function buildAllRoutes(param: BuildAllRoutesParam): Promise<RouteConfig[]>;
14
+ declare type CreateAllRoutesParam = {
15
+ baseUrl: string;
16
+ versions: FullVersion[];
17
+ options: PluginOptions;
12
18
  actions: PluginContentLoadedActions;
13
- docCategoryGeneratedIndexComponent: string;
14
19
  aliasedSource: (str: string) => string;
15
- }): Promise<RouteConfig[]>;
16
- export declare function createDocRoutes({ docs, actions, docItemComponent, }: {
17
- docs: DocMetadata[];
18
- actions: PluginContentLoadedActions;
19
- docItemComponent: string;
20
- }): Promise<RouteConfig[]>;
21
- export declare function createVersionRoutes({ version, actions, docItemComponent, docLayoutComponent, docCategoryGeneratedIndexComponent, pluginId, aliasedSource, }: {
22
- version: FullVersion;
23
- actions: PluginContentLoadedActions;
24
- docLayoutComponent: string;
25
- docItemComponent: string;
26
- docCategoryGeneratedIndexComponent: string;
27
- pluginId: string;
28
- aliasedSource: (str: string) => string;
29
- }): Promise<void>;
20
+ };
21
+ export declare function createAllRoutes(param: CreateAllRoutesParam): Promise<void>;
22
+ export {};
package/lib/routes.js CHANGED
@@ -6,20 +6,22 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.createVersionRoutes = exports.createDocRoutes = exports.createCategoryGeneratedIndexRoutes = void 0;
9
+ exports.createAllRoutes = exports.buildAllRoutes = void 0;
10
10
  const tslib_1 = require("tslib");
11
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
11
12
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
12
13
  const utils_1 = require("@docusaurus/utils");
13
14
  const props_1 = require("./props");
14
- async function createCategoryGeneratedIndexRoutes({ version, actions, docCategoryGeneratedIndexComponent, aliasedSource, }) {
15
+ const tags_1 = require("./tags");
16
+ async function buildVersionCategoryGeneratedIndexRoutes({ version, actions, options, aliasedSource, }) {
15
17
  const slugs = (0, utils_1.createSlugger)();
16
- async function createCategoryGeneratedIndexRoute(categoryGeneratedIndex) {
18
+ async function buildCategoryGeneratedIndexRoute(categoryGeneratedIndex) {
17
19
  const { sidebar, ...prop } = categoryGeneratedIndex;
18
20
  const propFileName = slugs.slug(`${version.path}-${categoryGeneratedIndex.sidebar}-category-${categoryGeneratedIndex.title}`);
19
21
  const propData = await actions.createData(`${(0, utils_1.docuHash)(`category/${propFileName}`)}.json`, JSON.stringify(prop, null, 2));
20
22
  return {
21
23
  path: categoryGeneratedIndex.permalink,
22
- component: docCategoryGeneratedIndexComponent,
24
+ component: options.docCategoryGeneratedIndexComponent,
23
25
  exact: true,
24
26
  modules: {
25
27
  categoryGeneratedIndex: aliasedSource(propData),
@@ -29,23 +31,22 @@ async function createCategoryGeneratedIndexRoutes({ version, actions, docCategor
29
31
  ...(sidebar && { sidebar }),
30
32
  };
31
33
  }
32
- return Promise.all(version.categoryGeneratedIndices.map(createCategoryGeneratedIndexRoute));
34
+ return Promise.all(version.categoryGeneratedIndices.map(buildCategoryGeneratedIndexRoute));
33
35
  }
34
- exports.createCategoryGeneratedIndexRoutes = createCategoryGeneratedIndexRoutes;
35
- async function createDocRoutes({ docs, actions, docItemComponent, }) {
36
- return Promise.all(docs.map(async (metadataItem) => {
36
+ async function buildVersionDocRoutes({ version, actions, options, }) {
37
+ return Promise.all(version.docs.map(async (metadataItem) => {
37
38
  await actions.createData(
38
39
  // Note that this created data path must be in sync with
39
40
  // metadataPath provided to mdx-loader.
40
41
  `${(0, utils_1.docuHash)(metadataItem.source)}.json`, JSON.stringify(metadataItem, null, 2));
41
42
  const docRoute = {
42
43
  path: metadataItem.permalink,
43
- component: docItemComponent,
44
+ component: options.docItemComponent,
44
45
  exact: true,
45
46
  modules: {
46
47
  content: metadataItem.source,
47
48
  },
48
- // Because the parent (DocPage) comp need to access it easily
49
+ // Because the parent (DocRoot) comp need to access it easily
49
50
  // This permits to render the sidebar once without unmount/remount when
50
51
  // navigating (and preserve sidebar state)
51
52
  ...(metadataItem.sidebar && {
@@ -55,42 +56,113 @@ async function createDocRoutes({ docs, actions, docItemComponent, }) {
55
56
  return docRoute;
56
57
  }));
57
58
  }
58
- exports.createDocRoutes = createDocRoutes;
59
- async function createVersionRoutes({ version, actions, docItemComponent, docLayoutComponent, docCategoryGeneratedIndexComponent, pluginId, aliasedSource, }) {
60
- async function doCreateVersionRoutes() {
61
- const versionMetadata = (0, props_1.toVersionMetadataProp)(pluginId, version);
62
- const versionMetadataPropPath = await actions.createData(`${(0, utils_1.docuHash)(`version-${version.versionName}-metadata-prop`)}.json`, JSON.stringify(versionMetadata, null, 2));
63
- async function createVersionSubRoutes() {
64
- const [docRoutes, sidebarsRoutes] = await Promise.all([
65
- createDocRoutes({ docs: version.docs, actions, docItemComponent }),
66
- createCategoryGeneratedIndexRoutes({
67
- version,
68
- actions,
69
- docCategoryGeneratedIndexComponent,
70
- aliasedSource,
71
- }),
72
- ]);
73
- const routes = [...docRoutes, ...sidebarsRoutes];
74
- return routes.sort((a, b) => a.path.localeCompare(b.path));
59
+ async function buildVersionSidebarRoute(param) {
60
+ const [docRoutes, categoryGeneratedIndexRoutes] = await Promise.all([
61
+ buildVersionDocRoutes(param),
62
+ buildVersionCategoryGeneratedIndexRoutes(param),
63
+ ]);
64
+ const subRoutes = [...docRoutes, ...categoryGeneratedIndexRoutes];
65
+ return {
66
+ path: param.version.path,
67
+ exact: false,
68
+ component: param.options.docRootComponent,
69
+ routes: subRoutes,
70
+ };
71
+ }
72
+ async function buildVersionTagsRoutes(param) {
73
+ const { version, options, actions, aliasedSource } = param;
74
+ const versionTags = (0, tags_1.getVersionTags)(version.docs);
75
+ async function buildTagsListRoute() {
76
+ // Don't create a tags list page if there's no tag
77
+ if (Object.keys(versionTags).length === 0) {
78
+ return null;
75
79
  }
76
- actions.addRoute({
80
+ const tagsProp = (0, props_1.toTagsListTagsProp)(versionTags);
81
+ const tagsPropPath = await actions.createData(`${(0, utils_1.docuHash)(`tags-list-${version.versionName}-prop`)}.json`, JSON.stringify(tagsProp, null, 2));
82
+ return {
83
+ path: version.tagsPath,
84
+ exact: true,
85
+ component: options.docTagsListComponent,
86
+ modules: {
87
+ tags: aliasedSource(tagsPropPath),
88
+ },
89
+ };
90
+ }
91
+ async function buildTagDocListRoute(tag) {
92
+ const tagProps = (0, props_1.toTagDocListProp)({
93
+ allTagsPath: version.tagsPath,
94
+ tag,
95
+ docs: version.docs,
96
+ });
97
+ const tagPropPath = await actions.createData(`${(0, utils_1.docuHash)(`tag-${tag.permalink}`)}.json`, JSON.stringify(tagProps, null, 2));
98
+ return {
99
+ path: tag.permalink,
100
+ component: options.docTagDocListComponent,
101
+ exact: true,
102
+ modules: {
103
+ tag: aliasedSource(tagPropPath),
104
+ },
105
+ };
106
+ }
107
+ const [tagsListRoute, allTagsDocListRoutes] = await Promise.all([
108
+ buildTagsListRoute(),
109
+ Promise.all(Object.values(versionTags).map(buildTagDocListRoute)),
110
+ ]);
111
+ return lodash_1.default.compact([tagsListRoute, ...allTagsDocListRoutes]);
112
+ }
113
+ async function buildVersionRoutes(param) {
114
+ const { version, actions, options, aliasedSource } = param;
115
+ async function buildVersionSubRoutes() {
116
+ const [sidebarRoute, tagsRoutes] = await Promise.all([
117
+ buildVersionSidebarRoute(param),
118
+ buildVersionTagsRoutes(param),
119
+ ]);
120
+ return [sidebarRoute, ...tagsRoutes];
121
+ }
122
+ async function doBuildVersionRoutes() {
123
+ const versionProp = (0, props_1.toVersionMetadataProp)(options.id, version);
124
+ const versionPropPath = await actions.createData(`${(0, utils_1.docuHash)(`version-${version.versionName}-metadata-prop`)}.json`, JSON.stringify(versionProp, null, 2));
125
+ const subRoutes = await buildVersionSubRoutes();
126
+ return {
77
127
  path: version.path,
78
- // Allow matching /docs/* since this is the wrapping route
79
128
  exact: false,
80
- component: docLayoutComponent,
81
- routes: await createVersionSubRoutes(),
129
+ component: options.docVersionRootComponent,
130
+ routes: subRoutes,
82
131
  modules: {
83
- versionMetadata: aliasedSource(versionMetadataPropPath),
132
+ version: aliasedSource(versionPropPath),
84
133
  },
85
134
  priority: version.routePriority,
86
- });
135
+ };
87
136
  }
88
137
  try {
89
- return await doCreateVersionRoutes();
138
+ return await doBuildVersionRoutes();
90
139
  }
91
140
  catch (err) {
92
141
  logger_1.default.error `Can't create version routes for version name=${version.versionName}`;
93
142
  throw err;
94
143
  }
95
144
  }
96
- exports.createVersionRoutes = createVersionRoutes;
145
+ // TODO we want this buildAllRoutes function to be easily testable
146
+ // Ideally, we should avoid side effects here (ie not injecting actions)
147
+ async function buildAllRoutes(param) {
148
+ const subRoutes = await Promise.all(param.versions.map((version) => buildVersionRoutes({
149
+ ...param,
150
+ version,
151
+ })));
152
+ // all docs routes are wrapped under a single parent route, this ensures
153
+ // the theme layout never unmounts/remounts when navigating between versions
154
+ return [
155
+ {
156
+ path: (0, utils_1.normalizeUrl)([param.baseUrl, param.options.routeBasePath]),
157
+ exact: false,
158
+ component: param.options.docsRootComponent,
159
+ routes: subRoutes,
160
+ },
161
+ ];
162
+ }
163
+ exports.buildAllRoutes = buildAllRoutes;
164
+ async function createAllRoutes(param) {
165
+ const routes = await buildAllRoutes(param);
166
+ routes.forEach(param.actions.addRoute);
167
+ }
168
+ exports.createAllRoutes = createAllRoutes;
@@ -4,8 +4,9 @@
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 type { PluginOptions, VersionBanner, VersionMetadata } from '@docusaurus/plugin-content-docs';
7
+ import type { FullVersion } from '../types';
8
8
  import type { LoadContext } from '@docusaurus/types';
9
+ import type { LoadedVersion, PluginOptions, VersionBanner, VersionMetadata } from '@docusaurus/plugin-content-docs';
9
10
  export declare type VersionContext = {
10
11
  /** The version name to get banner of. */
11
12
  versionName: string;
@@ -23,6 +24,7 @@ export declare type VersionContext = {
23
24
  export declare function getDefaultVersionBanner({ versionName, versionNames, lastVersionName, }: VersionContext): VersionBanner | null;
24
25
  export declare function getVersionBanner(context: VersionContext): VersionMetadata['banner'];
25
26
  export declare function getVersionBadge({ versionName, versionNames, options, }: VersionContext): VersionMetadata['badge'];
27
+ export declare function getVersionNoIndex({ versionName, options, }: VersionContext): VersionMetadata['noIndex'];
26
28
  /**
27
29
  * Filter versions according to provided options (i.e. `onlyIncludeVersions`).
28
30
  *
@@ -34,3 +36,4 @@ export declare function readVersionsMetadata({ context, options, }: {
34
36
  context: LoadContext;
35
37
  options: PluginOptions;
36
38
  }): Promise<VersionMetadata[]>;
39
+ export declare function toFullVersion(version: LoadedVersion): FullVersion;
@@ -6,13 +6,15 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.readVersionsMetadata = exports.filterVersions = exports.getVersionBadge = exports.getVersionBanner = exports.getDefaultVersionBanner = void 0;
9
+ exports.toFullVersion = exports.readVersionsMetadata = exports.filterVersions = exports.getVersionNoIndex = exports.getVersionBadge = exports.getVersionBanner = exports.getDefaultVersionBanner = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const path_1 = tslib_1.__importDefault(require("path"));
12
12
  const utils_1 = require("@docusaurus/utils");
13
13
  const constants_1 = require("../constants");
14
14
  const validation_1 = require("./validation");
15
15
  const files_1 = require("./files");
16
+ const utils_2 = require("../sidebars/utils");
17
+ const categoryGeneratedIndex_1 = require("../categoryGeneratedIndex");
16
18
  function getVersionEditUrls({ contentPath, contentPathLocalized, context, options, }) {
17
19
  // If the user is using the functional form of editUrl,
18
20
  // she has total freedom and we can't compute a "version edit url"
@@ -71,6 +73,10 @@ function getVersionBadge({ versionName, versionNames, options, }) {
71
73
  return options.versions[versionName]?.badge ?? defaultVersionBadge;
72
74
  }
73
75
  exports.getVersionBadge = getVersionBadge;
76
+ function getVersionNoIndex({ versionName, options, }) {
77
+ return options.versions[versionName]?.noIndex ?? false;
78
+ }
79
+ exports.getVersionNoIndex = getVersionNoIndex;
74
80
  function getVersionClassName({ versionName, options, }) {
75
81
  const defaultVersionClassName = `docs-version-${versionName}`;
76
82
  return options.versions[versionName]?.className ?? defaultVersionClassName;
@@ -108,6 +114,7 @@ async function createVersionMetadata(context) {
108
114
  label: getVersionLabel(context),
109
115
  banner: getVersionBanner(context),
110
116
  badge: getVersionBadge(context),
117
+ noIndex: getVersionNoIndex(context),
111
118
  className: getVersionClassName(context),
112
119
  path: routePath,
113
120
  tagsPath: (0, utils_1.normalizeUrl)([routePath, options.tagsBasePath]),
@@ -152,3 +159,15 @@ async function readVersionsMetadata({ context, options, }) {
152
159
  return versionsMetadata;
153
160
  }
154
161
  exports.readVersionsMetadata = readVersionsMetadata;
162
+ function toFullVersion(version) {
163
+ const sidebarsUtils = (0, utils_2.createSidebarsUtils)(version.sidebars);
164
+ return {
165
+ ...version,
166
+ sidebarsUtils,
167
+ categoryGeneratedIndices: (0, categoryGeneratedIndex_1.getCategoryGeneratedIndexMetadataList)({
168
+ docs: version.docs,
169
+ sidebarsUtils,
170
+ }),
171
+ };
172
+ }
173
+ exports.toFullVersion = toFullVersion;
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@docusaurus/plugin-content-docs",
3
- "version": "0.0.0-5262",
3
+ "version": "0.0.0-5265",
4
4
  "description": "Docs plugin for Docusaurus.",
5
5
  "main": "lib/index.js",
6
6
  "sideEffects": false,
7
7
  "exports": {
8
+ "./lib/*": "./lib/*",
8
9
  "./src/*": "./src/*",
9
10
  "./client": {
10
11
  "type": "./lib/client/index.d.ts",
@@ -34,13 +35,13 @@
34
35
  },
35
36
  "license": "MIT",
36
37
  "dependencies": {
37
- "@docusaurus/core": "0.0.0-5262",
38
- "@docusaurus/logger": "0.0.0-5262",
39
- "@docusaurus/mdx-loader": "0.0.0-5262",
40
- "@docusaurus/module-type-aliases": "0.0.0-5262",
41
- "@docusaurus/types": "0.0.0-5262",
42
- "@docusaurus/utils": "0.0.0-5262",
43
- "@docusaurus/utils-validation": "0.0.0-5262",
38
+ "@docusaurus/core": "0.0.0-5265",
39
+ "@docusaurus/logger": "0.0.0-5265",
40
+ "@docusaurus/mdx-loader": "0.0.0-5265",
41
+ "@docusaurus/module-type-aliases": "0.0.0-5265",
42
+ "@docusaurus/types": "0.0.0-5265",
43
+ "@docusaurus/utils": "0.0.0-5265",
44
+ "@docusaurus/utils-validation": "0.0.0-5265",
44
45
  "@types/react-router-config": "^5.0.6",
45
46
  "combine-promises": "^1.1.0",
46
47
  "fs-extra": "^10.1.0",
@@ -66,5 +67,5 @@
66
67
  "engines": {
67
68
  "node": ">=16.14"
68
69
  },
69
- "gitHead": "65823156f8eda86fc19a31742b7f7cae808a99a8"
70
+ "gitHead": "ac59d7c969c795c2163c8e8843c11d94e210f87f"
70
71
  }
package/src/index.ts CHANGED
@@ -27,22 +27,18 @@ import {
27
27
  addDocNavigation,
28
28
  type DocEnv,
29
29
  } from './docs';
30
- import {readVersionsMetadata} from './versions';
30
+ import {readVersionsMetadata, toFullVersion} from './versions';
31
31
  import {cliDocsVersionCommand} from './cli';
32
32
  import {VERSIONS_JSON_FILE} from './constants';
33
33
  import {toGlobalDataVersion} from './globalData';
34
- import {toTagDocListProp} from './props';
35
- import {getCategoryGeneratedIndexMetadataList} from './categoryGeneratedIndex';
36
34
  import {
37
35
  translateLoadedContent,
38
36
  getLoadedContentTranslationFiles,
39
37
  } from './translations';
40
- import {getVersionTags} from './tags';
41
- import {createVersionRoutes} from './routes';
38
+ import {createAllRoutes} from './routes';
42
39
  import {createSidebarsUtils} from './sidebars/utils';
43
40
 
44
41
  import type {
45
- PropTagsListPage,
46
42
  PluginOptions,
47
43
  DocMetadataBase,
48
44
  VersionMetadata,
@@ -55,7 +51,6 @@ import type {
55
51
  SourceToPermalink,
56
52
  DocFile,
57
53
  DocsMarkdownOption,
58
- VersionTag,
59
54
  FullVersion,
60
55
  } from './types';
61
56
  import type {RuleSetRule} from 'webpack';
@@ -209,102 +204,20 @@ export default async function pluginContentDocs(
209
204
  },
210
205
 
211
206
  async contentLoaded({content, actions}) {
212
- const {loadedVersions} = content;
213
- const {
214
- docLayoutComponent,
215
- docItemComponent,
216
- docCategoryGeneratedIndexComponent,
217
- breadcrumbs,
218
- } = options;
219
- const {addRoute, createData, setGlobalData} = actions;
220
- const versions: FullVersion[] = loadedVersions.map((version) => {
221
- const sidebarsUtils = createSidebarsUtils(version.sidebars);
222
- return {
223
- ...version,
224
- sidebarsUtils,
225
- categoryGeneratedIndices: getCategoryGeneratedIndexMetadataList({
226
- docs: version.docs,
227
- sidebarsUtils,
228
- }),
229
- };
207
+ const versions: FullVersion[] = content.loadedVersions.map(toFullVersion);
208
+
209
+ await createAllRoutes({
210
+ baseUrl,
211
+ versions,
212
+ options,
213
+ actions,
214
+ aliasedSource,
230
215
  });
231
216
 
232
- async function createVersionTagsRoutes(version: FullVersion) {
233
- const versionTags = getVersionTags(version.docs);
234
-
235
- // TODO tags should be a sub route of the version route
236
- async function createTagsListPage() {
237
- const tagsProp: PropTagsListPage['tags'] = Object.values(
238
- versionTags,
239
- ).map((tagValue) => ({
240
- label: tagValue.label,
241
- permalink: tagValue.permalink,
242
- count: tagValue.docIds.length,
243
- }));
244
-
245
- // Only create /tags page if there are tags.
246
- if (tagsProp.length > 0) {
247
- const tagsPropPath = await createData(
248
- `${docuHash(`tags-list-${version.versionName}-prop`)}.json`,
249
- JSON.stringify(tagsProp, null, 2),
250
- );
251
- addRoute({
252
- path: version.tagsPath,
253
- exact: true,
254
- component: options.docTagsListComponent,
255
- modules: {
256
- tags: aliasedSource(tagsPropPath),
257
- },
258
- });
259
- }
260
- }
261
-
262
- // TODO tags should be a sub route of the version route
263
- async function createTagDocListPage(tag: VersionTag) {
264
- const tagProps = toTagDocListProp({
265
- allTagsPath: version.tagsPath,
266
- tag,
267
- docs: version.docs,
268
- });
269
- const tagPropPath = await createData(
270
- `${docuHash(`tag-${tag.permalink}`)}.json`,
271
- JSON.stringify(tagProps, null, 2),
272
- );
273
- addRoute({
274
- path: tag.permalink,
275
- component: options.docTagDocListComponent,
276
- exact: true,
277
- modules: {
278
- tag: aliasedSource(tagPropPath),
279
- },
280
- });
281
- }
282
-
283
- await createTagsListPage();
284
- await Promise.all(Object.values(versionTags).map(createTagDocListPage));
285
- }
286
-
287
- await Promise.all(
288
- versions.map((version) =>
289
- createVersionRoutes({
290
- version,
291
- docItemComponent,
292
- docLayoutComponent,
293
- docCategoryGeneratedIndexComponent,
294
- pluginId,
295
- aliasedSource,
296
- actions,
297
- }),
298
- ),
299
- );
300
-
301
- // TODO tags should be a sub route of the version route
302
- await Promise.all(versions.map(createVersionTagsRoutes));
303
-
304
- setGlobalData({
217
+ actions.setGlobalData({
305
218
  path: normalizeUrl([baseUrl, options.routeBasePath]),
306
219
  versions: versions.map(toGlobalDataVersion),
307
- breadcrumbs,
220
+ breadcrumbs: options.breadcrumbs,
308
221
  });
309
222
  },
310
223
 
package/src/options.ts CHANGED
@@ -30,7 +30,9 @@ export const DEFAULT_OPTIONS: Omit<PluginOptions, 'id' | 'sidebarPath'> = {
30
30
  exclude: GlobExcludeDefault,
31
31
  sidebarItemsGenerator: DefaultSidebarItemsGenerator,
32
32
  numberPrefixParser: DefaultNumberPrefixParser,
33
- docLayoutComponent: '@theme/DocPage',
33
+ docsRootComponent: '@theme/DocsRoot',
34
+ docVersionRootComponent: '@theme/DocVersionRoot',
35
+ docRootComponent: '@theme/DocRoot',
34
36
  docItemComponent: '@theme/DocItem',
35
37
  docTagDocListComponent: '@theme/DocTagDocListPage',
36
38
  docTagsListComponent: '@theme/DocTagsListPage',
@@ -59,6 +61,7 @@ const VersionOptionsSchema = Joi.object({
59
61
  banner: Joi.string().equal('none', 'unreleased', 'unmaintained').optional(),
60
62
  badge: Joi.boolean().optional(),
61
63
  className: Joi.string().optional(),
64
+ noIndex: Joi.boolean().optional(),
62
65
  });
63
66
 
64
67
  const VersionsOptionsSchema = Joi.object()
@@ -103,7 +106,11 @@ const OptionsSchema = Joi.object<PluginOptions>({
103
106
  }),
104
107
  )
105
108
  .default(() => DEFAULT_OPTIONS.numberPrefixParser),
106
- docLayoutComponent: Joi.string().default(DEFAULT_OPTIONS.docLayoutComponent),
109
+ docsRootComponent: Joi.string().default(DEFAULT_OPTIONS.docsRootComponent),
110
+ docVersionRootComponent: Joi.string().default(
111
+ DEFAULT_OPTIONS.docVersionRootComponent,
112
+ ),
113
+ docRootComponent: Joi.string().default(DEFAULT_OPTIONS.docRootComponent),
107
114
  docItemComponent: Joi.string().default(DEFAULT_OPTIONS.docItemComponent),
108
115
  docTagsListComponent: Joi.string().default(
109
116
  DEFAULT_OPTIONS.docTagsListComponent,
@@ -125,6 +125,25 @@ declare module '@docusaurus/plugin-content-docs' {
125
125
  // TODO support custom version banner?
126
126
  // {type: "error", content: "html content"}
127
127
  export type VersionBanner = 'unreleased' | 'unmaintained';
128
+
129
+ export type VersionOptions = {
130
+ /**
131
+ * The base path of the version, will be appended to `baseUrl` +
132
+ * `routeBasePath`.
133
+ */
134
+ path?: string;
135
+ /** The label of the version to be used in badges, dropdowns, etc. */
136
+ label?: string;
137
+ /** The banner to show at the top of a doc of that version. */
138
+ banner?: 'none' | VersionBanner;
139
+ /** Show a badge with the version label at the top of each doc. */
140
+ badge?: boolean;
141
+ /** Prevents search engines from indexing this version */
142
+ noIndex?: boolean;
143
+ /** Add a custom class name to the <html> element of each doc. */
144
+ className?: string;
145
+ };
146
+
128
147
  export type VersionsOptions = {
129
148
  /**
130
149
  * The version navigated to in priority and displayed by default for docs
@@ -144,23 +163,7 @@ declare module '@docusaurus/plugin-content-docs' {
144
163
  /** Include the current version of your docs. */
145
164
  includeCurrentVersion: boolean;
146
165
  /** Independent customization of each version's properties. */
147
- versions: {
148
- [versionName: string]: {
149
- /**
150
- * The base path of the version, will be appended to `baseUrl` +
151
- * `routeBasePath`.
152
- */
153
- path?: string;
154
- /** The label of the version to be used in badges, dropdowns, etc. */
155
- label?: string;
156
- /** The banner to show at the top of a doc of that version. */
157
- banner?: 'none' | VersionBanner;
158
- /** Show a badge with the version label at the top of each doc. */
159
- badge?: boolean;
160
- /** Add a custom class name to the <html> element of each doc. */
161
- className?: string;
162
- };
163
- };
166
+ versions: {[versionName: string]: VersionOptions};
164
167
  };
165
168
  export type SidebarOptions = {
166
169
  /**
@@ -195,10 +198,24 @@ declare module '@docusaurus/plugin-content-docs' {
195
198
  */
196
199
  exclude: string[];
197
200
  /**
198
- * Root layout component of each doc page. Provides the version data
199
- * context, and is not unmounted when switching docs.
201
+ * Parent component of all the docs plugin pages (including all versions).
202
+ * Stays mounted when navigation between docs pages and versions.
203
+ */
204
+ docsRootComponent: string;
205
+ /**
206
+ * Parent component of all docs pages of an individual version:
207
+ * - docs pages with sidebars
208
+ * - tags pages
209
+ * Stays mounted when navigation between pages of that specific version.
210
+ */
211
+ docVersionRootComponent: string;
212
+ /**
213
+ * Parent component of all docs pages with sidebars:
214
+ * - regular docs pages
215
+ * - category generated index pages
216
+ * Stays mounted when navigation between such pages.
200
217
  */
201
- docLayoutComponent: string;
218
+ docRootComponent: string;
202
219
  /** Main doc container, with TOC, pagination, etc. */
203
220
  docItemComponent: string;
204
221
  /** Root component of the "docs containing tag X" page. */
@@ -263,6 +280,8 @@ declare module '@docusaurus/plugin-content-docs' {
263
280
  banner: VersionBanner | null;
264
281
  /** Show a badge with the version label at the top of each doc. */
265
282
  badge: boolean;
283
+ /** Prevents search engines from indexing this version */
284
+ noIndex: boolean;
266
285
  /** Add a custom class name to the <html> element of each doc. */
267
286
  className: string;
268
287
  /**
@@ -500,7 +519,7 @@ declare module '@docusaurus/plugin-content-docs' {
500
519
 
501
520
  export type PropVersionMetadata = Pick<
502
521
  VersionMetadata,
503
- 'label' | 'banner' | 'badge' | 'className' | 'isLast'
522
+ 'label' | 'banner' | 'badge' | 'className' | 'isLast' | 'noIndex'
504
523
  > & {
505
524
  /** ID of the docs plugin this version belongs to. */
506
525
  pluginId: string;
@@ -605,14 +624,32 @@ declare module '@theme/DocBreadcrumbs' {
605
624
  export default function DocBreadcrumbs(): JSX.Element;
606
625
  }
607
626
 
608
- declare module '@theme/DocPage' {
627
+ declare module '@theme/DocsRoot' {
628
+ import type {RouteConfigComponentProps} from 'react-router-config';
629
+ import type {Required} from 'utility-types';
630
+
631
+ export interface Props extends Required<RouteConfigComponentProps, 'route'> {}
632
+
633
+ export default function DocsRoot(props: Props): JSX.Element;
634
+ }
635
+
636
+ declare module '@theme/DocVersionRoot' {
609
637
  import type {PropVersionMetadata} from '@docusaurus/plugin-content-docs';
610
638
  import type {RouteConfigComponentProps} from 'react-router-config';
611
639
  import type {Required} from 'utility-types';
612
640
 
613
641
  export interface Props extends Required<RouteConfigComponentProps, 'route'> {
614
- readonly versionMetadata: PropVersionMetadata;
642
+ readonly version: PropVersionMetadata;
615
643
  }
616
644
 
617
- export default function DocPage(props: Props): JSX.Element;
645
+ export default function DocVersionRoot(props: Props): JSX.Element;
646
+ }
647
+
648
+ declare module '@theme/DocRoot' {
649
+ import type {RouteConfigComponentProps} from 'react-router-config';
650
+ import type {Required} from 'utility-types';
651
+
652
+ export interface Props extends Required<RouteConfigComponentProps, 'route'> {}
653
+
654
+ export default function DocRoot(props: Props): JSX.Element;
618
655
  }
package/src/props.ts CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  import _ from 'lodash';
9
9
  import {createDocsByIdIndex} from './docs';
10
- import type {VersionTag} from './types';
10
+ import type {VersionTag, VersionTags} from './types';
11
11
  import type {
12
12
  SidebarItemDoc,
13
13
  SidebarItem,
@@ -21,6 +21,7 @@ import type {
21
21
  PropSidebarItemCategory,
22
22
  PropTagDocList,
23
23
  PropTagDocListDoc,
24
+ PropTagsListPage,
24
25
  PropSidebarItemLink,
25
26
  PropVersionDocs,
26
27
  DocMetadata,
@@ -142,6 +143,7 @@ export function toVersionMetadataProp(
142
143
  label: loadedVersion.label,
143
144
  banner: loadedVersion.banner,
144
145
  badge: loadedVersion.badge,
146
+ noIndex: loadedVersion.noIndex,
145
147
  className: loadedVersion.className,
146
148
  isLast: loadedVersion.isLast,
147
149
  docsSidebars: toSidebarsProp(loadedVersion),
@@ -180,3 +182,13 @@ export function toTagDocListProp({
180
182
  items: toDocListProp(),
181
183
  };
182
184
  }
185
+
186
+ export function toTagsListTagsProp(
187
+ versionTags: VersionTags,
188
+ ): PropTagsListPage['tags'] {
189
+ return Object.values(versionTags).map((tagValue) => ({
190
+ label: tagValue.label,
191
+ permalink: tagValue.permalink,
192
+ count: tagValue.docIds.length,
193
+ }));
194
+ }
package/src/routes.ts CHANGED
@@ -5,30 +5,32 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
 
8
+ import _ from 'lodash';
8
9
  import logger from '@docusaurus/logger';
9
- import {docuHash, createSlugger} from '@docusaurus/utils';
10
- import {toVersionMetadataProp} from './props';
10
+ import {docuHash, createSlugger, normalizeUrl} from '@docusaurus/utils';
11
+ import {
12
+ toTagDocListProp,
13
+ toTagsListTagsProp,
14
+ toVersionMetadataProp,
15
+ } from './props';
16
+ import {getVersionTags} from './tags';
11
17
  import type {PluginContentLoadedActions, RouteConfig} from '@docusaurus/types';
12
- import type {FullVersion} from './types';
18
+ import type {FullVersion, VersionTag} from './types';
13
19
  import type {
14
20
  CategoryGeneratedIndexMetadata,
15
- DocMetadata,
21
+ PluginOptions,
22
+ PropTagsListPage,
16
23
  } from '@docusaurus/plugin-content-docs';
17
24
 
18
- export async function createCategoryGeneratedIndexRoutes({
25
+ async function buildVersionCategoryGeneratedIndexRoutes({
19
26
  version,
20
27
  actions,
21
- docCategoryGeneratedIndexComponent,
28
+ options,
22
29
  aliasedSource,
23
- }: {
24
- version: FullVersion;
25
- actions: PluginContentLoadedActions;
26
- docCategoryGeneratedIndexComponent: string;
27
- aliasedSource: (str: string) => string;
28
- }): Promise<RouteConfig[]> {
30
+ }: BuildVersionRoutesParam): Promise<RouteConfig[]> {
29
31
  const slugs = createSlugger();
30
32
 
31
- async function createCategoryGeneratedIndexRoute(
33
+ async function buildCategoryGeneratedIndexRoute(
32
34
  categoryGeneratedIndex: CategoryGeneratedIndexMetadata,
33
35
  ): Promise<RouteConfig> {
34
36
  const {sidebar, ...prop} = categoryGeneratedIndex;
@@ -44,7 +46,7 @@ export async function createCategoryGeneratedIndexRoutes({
44
46
 
45
47
  return {
46
48
  path: categoryGeneratedIndex.permalink,
47
- component: docCategoryGeneratedIndexComponent,
49
+ component: options.docCategoryGeneratedIndexComponent,
48
50
  exact: true,
49
51
  modules: {
50
52
  categoryGeneratedIndex: aliasedSource(propData),
@@ -56,21 +58,17 @@ export async function createCategoryGeneratedIndexRoutes({
56
58
  }
57
59
 
58
60
  return Promise.all(
59
- version.categoryGeneratedIndices.map(createCategoryGeneratedIndexRoute),
61
+ version.categoryGeneratedIndices.map(buildCategoryGeneratedIndexRoute),
60
62
  );
61
63
  }
62
64
 
63
- export async function createDocRoutes({
64
- docs,
65
+ async function buildVersionDocRoutes({
66
+ version,
65
67
  actions,
66
- docItemComponent,
67
- }: {
68
- docs: DocMetadata[];
69
- actions: PluginContentLoadedActions;
70
- docItemComponent: string;
71
- }): Promise<RouteConfig[]> {
68
+ options,
69
+ }: BuildVersionRoutesParam): Promise<RouteConfig[]> {
72
70
  return Promise.all(
73
- docs.map(async (metadataItem) => {
71
+ version.docs.map(async (metadataItem) => {
74
72
  await actions.createData(
75
73
  // Note that this created data path must be in sync with
76
74
  // metadataPath provided to mdx-loader.
@@ -80,12 +78,12 @@ export async function createDocRoutes({
80
78
 
81
79
  const docRoute: RouteConfig = {
82
80
  path: metadataItem.permalink,
83
- component: docItemComponent,
81
+ component: options.docItemComponent,
84
82
  exact: true,
85
83
  modules: {
86
84
  content: metadataItem.source,
87
85
  },
88
- // Because the parent (DocPage) comp need to access it easily
86
+ // Because the parent (DocRoot) comp need to access it easily
89
87
  // This permits to render the sidebar once without unmount/remount when
90
88
  // navigating (and preserve sidebar state)
91
89
  ...(metadataItem.sidebar && {
@@ -98,62 +96,160 @@ export async function createDocRoutes({
98
96
  );
99
97
  }
100
98
 
101
- export async function createVersionRoutes({
102
- version,
103
- actions,
104
- docItemComponent,
105
- docLayoutComponent,
106
- docCategoryGeneratedIndexComponent,
107
- pluginId,
108
- aliasedSource,
109
- }: {
110
- version: FullVersion;
111
- actions: PluginContentLoadedActions;
112
- docLayoutComponent: string;
113
- docItemComponent: string;
114
- docCategoryGeneratedIndexComponent: string;
115
- pluginId: string;
116
- aliasedSource: (str: string) => string;
117
- }): Promise<void> {
118
- async function doCreateVersionRoutes(): Promise<void> {
119
- const versionMetadata = toVersionMetadataProp(pluginId, version);
120
- const versionMetadataPropPath = await actions.createData(
121
- `${docuHash(`version-${version.versionName}-metadata-prop`)}.json`,
122
- JSON.stringify(versionMetadata, null, 2),
123
- );
99
+ async function buildVersionSidebarRoute(param: BuildVersionRoutesParam) {
100
+ const [docRoutes, categoryGeneratedIndexRoutes] = await Promise.all([
101
+ buildVersionDocRoutes(param),
102
+ buildVersionCategoryGeneratedIndexRoutes(param),
103
+ ]);
104
+ const subRoutes = [...docRoutes, ...categoryGeneratedIndexRoutes];
105
+ return {
106
+ path: param.version.path,
107
+ exact: false,
108
+ component: param.options.docRootComponent,
109
+ routes: subRoutes,
110
+ };
111
+ }
124
112
 
125
- async function createVersionSubRoutes() {
126
- const [docRoutes, sidebarsRoutes] = await Promise.all([
127
- createDocRoutes({docs: version.docs, actions, docItemComponent}),
128
- createCategoryGeneratedIndexRoutes({
129
- version,
130
- actions,
131
- docCategoryGeneratedIndexComponent,
132
- aliasedSource,
133
- }),
134
- ]);
113
+ async function buildVersionTagsRoutes(
114
+ param: BuildVersionRoutesParam,
115
+ ): Promise<RouteConfig[]> {
116
+ const {version, options, actions, aliasedSource} = param;
117
+ const versionTags = getVersionTags(version.docs);
135
118
 
136
- const routes = [...docRoutes, ...sidebarsRoutes];
137
- return routes.sort((a, b) => a.path.localeCompare(b.path));
119
+ async function buildTagsListRoute(): Promise<RouteConfig | null> {
120
+ // Don't create a tags list page if there's no tag
121
+ if (Object.keys(versionTags).length === 0) {
122
+ return null;
138
123
  }
124
+ const tagsProp: PropTagsListPage['tags'] = toTagsListTagsProp(versionTags);
125
+ const tagsPropPath = await actions.createData(
126
+ `${docuHash(`tags-list-${version.versionName}-prop`)}.json`,
127
+ JSON.stringify(tagsProp, null, 2),
128
+ );
129
+ return {
130
+ path: version.tagsPath,
131
+ exact: true,
132
+ component: options.docTagsListComponent,
133
+ modules: {
134
+ tags: aliasedSource(tagsPropPath),
135
+ },
136
+ };
137
+ }
138
+
139
+ async function buildTagDocListRoute(tag: VersionTag): Promise<RouteConfig> {
140
+ const tagProps = toTagDocListProp({
141
+ allTagsPath: version.tagsPath,
142
+ tag,
143
+ docs: version.docs,
144
+ });
145
+ const tagPropPath = await actions.createData(
146
+ `${docuHash(`tag-${tag.permalink}`)}.json`,
147
+ JSON.stringify(tagProps, null, 2),
148
+ );
149
+ return {
150
+ path: tag.permalink,
151
+ component: options.docTagDocListComponent,
152
+ exact: true,
153
+ modules: {
154
+ tag: aliasedSource(tagPropPath),
155
+ },
156
+ };
157
+ }
158
+
159
+ const [tagsListRoute, allTagsDocListRoutes] = await Promise.all([
160
+ buildTagsListRoute(),
161
+ Promise.all(Object.values(versionTags).map(buildTagDocListRoute)),
162
+ ]);
163
+
164
+ return _.compact([tagsListRoute, ...allTagsDocListRoutes]);
165
+ }
166
+
167
+ type BuildVersionRoutesParam = Omit<BuildAllRoutesParam, 'versions'> & {
168
+ version: FullVersion;
169
+ };
170
+
171
+ async function buildVersionRoutes(
172
+ param: BuildVersionRoutesParam,
173
+ ): Promise<RouteConfig> {
174
+ const {version, actions, options, aliasedSource} = param;
139
175
 
140
- actions.addRoute({
176
+ async function buildVersionSubRoutes() {
177
+ const [sidebarRoute, tagsRoutes] = await Promise.all([
178
+ buildVersionSidebarRoute(param),
179
+ buildVersionTagsRoutes(param),
180
+ ]);
181
+
182
+ return [sidebarRoute, ...tagsRoutes];
183
+ }
184
+
185
+ async function doBuildVersionRoutes(): Promise<RouteConfig> {
186
+ const versionProp = toVersionMetadataProp(options.id, version);
187
+ const versionPropPath = await actions.createData(
188
+ `${docuHash(`version-${version.versionName}-metadata-prop`)}.json`,
189
+ JSON.stringify(versionProp, null, 2),
190
+ );
191
+ const subRoutes = await buildVersionSubRoutes();
192
+ return {
141
193
  path: version.path,
142
- // Allow matching /docs/* since this is the wrapping route
143
194
  exact: false,
144
- component: docLayoutComponent,
145
- routes: await createVersionSubRoutes(),
195
+ component: options.docVersionRootComponent,
196
+ routes: subRoutes,
146
197
  modules: {
147
- versionMetadata: aliasedSource(versionMetadataPropPath),
198
+ version: aliasedSource(versionPropPath),
148
199
  },
149
200
  priority: version.routePriority,
150
- });
201
+ };
151
202
  }
152
203
 
153
204
  try {
154
- return await doCreateVersionRoutes();
205
+ return await doBuildVersionRoutes();
155
206
  } catch (err) {
156
207
  logger.error`Can't create version routes for version name=${version.versionName}`;
157
208
  throw err;
158
209
  }
159
210
  }
211
+
212
+ type BuildAllRoutesParam = Omit<CreateAllRoutesParam, 'actions'> & {
213
+ actions: Omit<PluginContentLoadedActions, 'addRoute' | 'setGlobalData'>;
214
+ };
215
+
216
+ // TODO we want this buildAllRoutes function to be easily testable
217
+ // Ideally, we should avoid side effects here (ie not injecting actions)
218
+ export async function buildAllRoutes(
219
+ param: BuildAllRoutesParam,
220
+ ): Promise<RouteConfig[]> {
221
+ const subRoutes = await Promise.all(
222
+ param.versions.map((version) =>
223
+ buildVersionRoutes({
224
+ ...param,
225
+ version,
226
+ }),
227
+ ),
228
+ );
229
+
230
+ // all docs routes are wrapped under a single parent route, this ensures
231
+ // the theme layout never unmounts/remounts when navigating between versions
232
+ return [
233
+ {
234
+ path: normalizeUrl([param.baseUrl, param.options.routeBasePath]),
235
+ exact: false,
236
+ component: param.options.docsRootComponent,
237
+ routes: subRoutes,
238
+ },
239
+ ];
240
+ }
241
+
242
+ type CreateAllRoutesParam = {
243
+ baseUrl: string;
244
+ versions: FullVersion[];
245
+ options: PluginOptions;
246
+ actions: PluginContentLoadedActions;
247
+ aliasedSource: (str: string) => string;
248
+ };
249
+
250
+ export async function createAllRoutes(
251
+ param: CreateAllRoutesParam,
252
+ ): Promise<void> {
253
+ const routes = await buildAllRoutes(param);
254
+ routes.forEach(param.actions.addRoute);
255
+ }
@@ -14,12 +14,16 @@ import {
14
14
  getVersionMetadataPaths,
15
15
  readVersionNames,
16
16
  } from './files';
17
+ import {createSidebarsUtils} from '../sidebars/utils';
18
+ import {getCategoryGeneratedIndexMetadataList} from '../categoryGeneratedIndex';
19
+ import type {FullVersion} from '../types';
20
+ import type {LoadContext} from '@docusaurus/types';
17
21
  import type {
22
+ LoadedVersion,
18
23
  PluginOptions,
19
24
  VersionBanner,
20
25
  VersionMetadata,
21
26
  } from '@docusaurus/plugin-content-docs';
22
- import type {LoadContext} from '@docusaurus/types';
23
27
 
24
28
  export type VersionContext = {
25
29
  /** The version name to get banner of. */
@@ -122,6 +126,13 @@ export function getVersionBadge({
122
126
  return options.versions[versionName]?.badge ?? defaultVersionBadge;
123
127
  }
124
128
 
129
+ export function getVersionNoIndex({
130
+ versionName,
131
+ options,
132
+ }: VersionContext): VersionMetadata['noIndex'] {
133
+ return options.versions[versionName]?.noIndex ?? false;
134
+ }
135
+
125
136
  function getVersionClassName({
126
137
  versionName,
127
138
  options,
@@ -179,6 +190,7 @@ async function createVersionMetadata(
179
190
  label: getVersionLabel(context),
180
191
  banner: getVersionBanner(context),
181
192
  badge: getVersionBadge(context),
193
+ noIndex: getVersionNoIndex(context),
182
194
  className: getVersionClassName(context),
183
195
  path: routePath,
184
196
  tagsPath: normalizeUrl([routePath, options.tagsBasePath]),
@@ -244,3 +256,15 @@ export async function readVersionsMetadata({
244
256
  );
245
257
  return versionsMetadata;
246
258
  }
259
+
260
+ export function toFullVersion(version: LoadedVersion): FullVersion {
261
+ const sidebarsUtils = createSidebarsUtils(version.sidebars);
262
+ return {
263
+ ...version,
264
+ sidebarsUtils,
265
+ categoryGeneratedIndices: getCategoryGeneratedIndexMetadataList({
266
+ docs: version.docs,
267
+ sidebarsUtils,
268
+ }),
269
+ };
270
+ }