@azerion/bunosaurus-plugin-content-docs 0.0.0-stage → 4.0.0

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 (119) hide show
  1. package/README.md +6 -2
  2. package/lib/categoryGeneratedIndex.d.ts +12 -0
  3. package/lib/categoryGeneratedIndex.js +36 -0
  4. package/lib/cli.d.ts +13 -0
  5. package/lib/cli.js +124 -0
  6. package/lib/client/doc.d.ts +29 -0
  7. package/lib/client/doc.js +47 -0
  8. package/lib/client/docSidebarItemsExpandedState.d.ts +30 -0
  9. package/lib/client/docSidebarItemsExpandedState.js +27 -0
  10. package/lib/client/docsClientUtils.d.ts +15 -0
  11. package/lib/client/docsClientUtils.js +86 -0
  12. package/lib/client/docsPreferredVersion.d.ts +29 -0
  13. package/lib/client/docsPreferredVersion.js +124 -0
  14. package/lib/client/docsSearch.d.ts +19 -0
  15. package/lib/client/docsSearch.js +39 -0
  16. package/lib/client/docsSidebar.d.ts +25 -0
  17. package/lib/client/docsSidebar.js +29 -0
  18. package/lib/client/docsUtils.d.ts +112 -0
  19. package/lib/client/docsUtils.js +299 -0
  20. package/lib/client/docsVersion.d.ts +19 -0
  21. package/lib/client/docsVersion.js +25 -0
  22. package/lib/client/index.d.ts +91 -0
  23. package/lib/client/index.js +83 -0
  24. package/lib/client/structuredDataUtils.d.ts +11 -0
  25. package/lib/client/structuredDataUtils.js +35 -0
  26. package/lib/constants.d.ts +14 -0
  27. package/lib/constants.js +17 -0
  28. package/lib/contentHelpers.d.ts +12 -0
  29. package/lib/contentHelpers.js +31 -0
  30. package/lib/docs.d.ts +48 -0
  31. package/lib/docs.js +252 -0
  32. package/lib/frontMatter.d.ts +10 -0
  33. package/lib/frontMatter.js +46 -0
  34. package/lib/globalData.d.ts +9 -0
  35. package/lib/globalData.js +59 -0
  36. package/lib/index.d.ts +10 -0
  37. package/lib/index.js +198 -0
  38. package/lib/numberPrefix.d.ts +11 -0
  39. package/lib/numberPrefix.js +50 -0
  40. package/lib/options.d.ts +10 -0
  41. package/lib/options.js +138 -0
  42. package/lib/props.d.ts +21 -0
  43. package/lib/props.js +156 -0
  44. package/lib/routes.d.ts +22 -0
  45. package/lib/routes.js +184 -0
  46. package/lib/server-export.d.ts +9 -0
  47. package/lib/server-export.js +25 -0
  48. package/lib/sidebars/generator.d.ts +10 -0
  49. package/lib/sidebars/generator.js +217 -0
  50. package/lib/sidebars/index.d.ts +13 -0
  51. package/lib/sidebars/index.js +99 -0
  52. package/lib/sidebars/normalization.d.ts +13 -0
  53. package/lib/sidebars/normalization.js +58 -0
  54. package/lib/sidebars/postProcessor.d.ts +11 -0
  55. package/lib/sidebars/postProcessor.js +82 -0
  56. package/lib/sidebars/processor.d.ts +10 -0
  57. package/lib/sidebars/processor.js +77 -0
  58. package/lib/sidebars/types.d.ts +192 -0
  59. package/lib/sidebars/types.js +8 -0
  60. package/lib/sidebars/utils.d.ts +69 -0
  61. package/lib/sidebars/utils.js +327 -0
  62. package/lib/sidebars/validation.d.ts +11 -0
  63. package/lib/sidebars/validation.js +152 -0
  64. package/lib/slug.d.ts +15 -0
  65. package/lib/slug.js +51 -0
  66. package/lib/translations.d.ts +10 -0
  67. package/lib/translations.js +243 -0
  68. package/lib/types.d.ts +27 -0
  69. package/lib/types.js +8 -0
  70. package/lib/versions/files.d.ts +54 -0
  71. package/lib/versions/files.js +152 -0
  72. package/lib/versions/loadVersion.d.ts +17 -0
  73. package/lib/versions/loadVersion.js +126 -0
  74. package/lib/versions/validation.d.ts +17 -0
  75. package/lib/versions/validation.js +70 -0
  76. package/lib/versions/version.d.ts +40 -0
  77. package/lib/versions/version.js +189 -0
  78. package/package.json +70 -4
  79. package/src/categoryGeneratedIndex.ts +60 -0
  80. package/src/cli.ts +180 -0
  81. package/src/client/doc.tsx +71 -0
  82. package/src/client/docSidebarItemsExpandedState.tsx +55 -0
  83. package/src/client/docsClientUtils.ts +140 -0
  84. package/src/client/docsPreferredVersion.tsx +245 -0
  85. package/src/client/docsSearch.test.ts +15 -0
  86. package/src/client/docsSearch.ts +57 -0
  87. package/src/client/docsSidebar.tsx +50 -0
  88. package/src/client/docsUtils.tsx +442 -0
  89. package/src/client/docsVersion.tsx +36 -0
  90. package/src/client/index.ts +211 -0
  91. package/src/client/structuredDataUtils.ts +54 -0
  92. package/src/constants.ts +15 -0
  93. package/src/contentHelpers.ts +34 -0
  94. package/src/docs.ts +402 -0
  95. package/src/frontMatter.ts +57 -0
  96. package/src/globalData.ts +79 -0
  97. package/src/index.ts +289 -0
  98. package/src/numberPrefix.ts +62 -0
  99. package/src/options.ts +185 -0
  100. package/src/plugin-content-docs.d.ts +667 -0
  101. package/src/props.ts +223 -0
  102. package/src/routes.ts +263 -0
  103. package/src/server-export.ts +26 -0
  104. package/src/sidebars/README.md +10 -0
  105. package/src/sidebars/generator.ts +307 -0
  106. package/src/sidebars/index.ts +130 -0
  107. package/src/sidebars/normalization.ts +91 -0
  108. package/src/sidebars/postProcessor.ts +115 -0
  109. package/src/sidebars/processor.ts +122 -0
  110. package/src/sidebars/types.ts +293 -0
  111. package/src/sidebars/utils.ts +529 -0
  112. package/src/sidebars/validation.ts +189 -0
  113. package/src/slug.ts +79 -0
  114. package/src/translations.ts +365 -0
  115. package/src/types.ts +34 -0
  116. package/src/versions/files.ts +240 -0
  117. package/src/versions/loadVersion.ts +185 -0
  118. package/src/versions/validation.ts +115 -0
  119. package/src/versions/version.ts +300 -0
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.validateVersionName = validateVersionName;
10
+ exports.validateVersionNames = validateVersionNames;
11
+ exports.validateVersionsOptions = validateVersionsOptions;
12
+ const tslib_1 = require("tslib");
13
+ const lodash_1 = tslib_1.__importDefault(require("lodash"));
14
+ function validateVersionName(name) {
15
+ if (typeof name !== 'string') {
16
+ throw new Error(`Versions should be strings. Found type "${typeof name}" for version ${JSON.stringify(name)}.`);
17
+ }
18
+ if (!name.trim()) {
19
+ throw new Error(`Invalid version name "${name}": version name must contain at least one non-whitespace character.`);
20
+ }
21
+ const errors = [
22
+ [/[/\\]/, 'should not include slash (/) or backslash (\\)'],
23
+ [/.{33,}/, 'cannot be longer than 32 characters'],
24
+ // eslint-disable-next-line no-control-regex
25
+ [/[<>:"|?*\x00-\x1F]/, 'should be a valid file path'],
26
+ [/^\.\.?$/, 'should not be "." or ".."'],
27
+ ];
28
+ errors.forEach(([pattern, message]) => {
29
+ if (pattern.test(name)) {
30
+ throw new Error(`Invalid version name "${name}": version name ${message}.`);
31
+ }
32
+ });
33
+ }
34
+ function validateVersionNames(names) {
35
+ if (!Array.isArray(names)) {
36
+ throw new Error(`The versions file should contain an array of version names! Found content: ${JSON.stringify(names)}`);
37
+ }
38
+ names.forEach(validateVersionName);
39
+ }
40
+ /**
41
+ * @throws Throws for one of the following invalid options:
42
+ * - `lastVersion` is non-existent
43
+ * - `versions` includes unknown keys
44
+ * - `onlyIncludeVersions` is empty, contains unknown names, or doesn't include
45
+ * `latestVersion` (if provided)
46
+ */
47
+ function validateVersionsOptions(availableVersionNames, options) {
48
+ const availableVersionNamesMsg = `Available version names are: ${availableVersionNames.join(', ')}`;
49
+ if (options.lastVersion &&
50
+ !availableVersionNames.includes(options.lastVersion)) {
51
+ throw new Error(`Docs option lastVersion: ${options.lastVersion} is invalid. ${availableVersionNamesMsg}`);
52
+ }
53
+ const unknownVersionConfigNames = lodash_1.default.difference(Object.keys(options.versions), availableVersionNames);
54
+ if (unknownVersionConfigNames.length > 0) {
55
+ throw new Error(`Invalid docs option "versions": unknown versions (${unknownVersionConfigNames.join(',')}) found. ${availableVersionNamesMsg}`);
56
+ }
57
+ if (options.onlyIncludeVersions) {
58
+ if (options.onlyIncludeVersions.length === 0) {
59
+ throw new Error(`Invalid docs option "onlyIncludeVersions": an empty array is not allowed, at least one version is needed.`);
60
+ }
61
+ const unknownOnlyIncludeVersionNames = lodash_1.default.difference(options.onlyIncludeVersions, availableVersionNames);
62
+ if (unknownOnlyIncludeVersionNames.length > 0) {
63
+ throw new Error(`Invalid docs option "onlyIncludeVersions": unknown versions (${unknownOnlyIncludeVersionNames.join(',')}) found. ${availableVersionNamesMsg}`);
64
+ }
65
+ if (options.lastVersion &&
66
+ !options.onlyIncludeVersions.includes(options.lastVersion)) {
67
+ throw new Error(`Invalid docs option "lastVersion": if you use both the "onlyIncludeVersions" and "lastVersion" options, then "lastVersion" must be present in the provided "onlyIncludeVersions" array.`);
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import type { FullVersion } from '../types';
8
+ import type { LoadContext } from '@docusaurus/types';
9
+ import type { LoadedVersion, PluginOptions, VersionBanner, VersionMetadata } from '@docusaurus/plugin-content-docs';
10
+ export type VersionContext = {
11
+ /** The version name to get banner of. */
12
+ versionName: string;
13
+ /** All versions, ordered from newest to oldest. */
14
+ versionNames: string[];
15
+ lastVersionName: string;
16
+ context: LoadContext;
17
+ options: PluginOptions;
18
+ };
19
+ /**
20
+ * The default version banner depends on the version's relative position to the
21
+ * latest version. More recent ones are "unreleased", and older ones are
22
+ * "unmaintained".
23
+ */
24
+ export declare function getDefaultVersionBanner({ versionName, versionNames, lastVersionName, }: VersionContext): VersionBanner | null;
25
+ export declare function getVersionBanner(context: VersionContext): VersionMetadata['banner'];
26
+ export declare function getVersionBadge({ versionName, versionNames, options, }: VersionContext): VersionMetadata['badge'];
27
+ export declare function getVersionNoIndex({ versionName, options, }: VersionContext): VersionMetadata['noIndex'];
28
+ /**
29
+ * Filter versions according to provided options (i.e. `onlyIncludeVersions`).
30
+ *
31
+ * Note: we preserve the order in which versions are provided; the order of the
32
+ * `onlyIncludeVersions` array does not matter
33
+ */
34
+ export declare function filterVersions(versionNamesUnfiltered: string[], options: PluginOptions): string[];
35
+ export declare function readVersionsMetadata({ context, options, }: {
36
+ context: LoadContext;
37
+ options: PluginOptions;
38
+ }): Promise<VersionMetadata[]>;
39
+ export declare function toFullVersion(version: LoadedVersion): FullVersion;
40
+ export declare function getVersionFromSourceFilePath(filePath: string, versionsMetadata: VersionMetadata[]): VersionMetadata;
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getDefaultVersionBanner = getDefaultVersionBanner;
10
+ exports.getVersionBanner = getVersionBanner;
11
+ exports.getVersionBadge = getVersionBadge;
12
+ exports.getVersionNoIndex = getVersionNoIndex;
13
+ exports.filterVersions = filterVersions;
14
+ exports.readVersionsMetadata = readVersionsMetadata;
15
+ exports.toFullVersion = toFullVersion;
16
+ exports.getVersionFromSourceFilePath = getVersionFromSourceFilePath;
17
+ const tslib_1 = require("tslib");
18
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
19
+ const utils_1 = require("@docusaurus/utils");
20
+ const constants_1 = require("../constants");
21
+ const validation_1 = require("./validation");
22
+ const files_1 = require("./files");
23
+ const utils_2 = require("../sidebars/utils");
24
+ const categoryGeneratedIndex_1 = require("../categoryGeneratedIndex");
25
+ function getVersionEditUrls({ contentPath, contentPathLocalized, context, options, }) {
26
+ // If the user is using the functional form of editUrl,
27
+ // she has total freedom and we can't compute a "version edit url"
28
+ if (!options.editUrl || typeof options.editUrl === 'function') {
29
+ return { editUrl: undefined, editUrlLocalized: undefined };
30
+ }
31
+ // Intermediate var just to please TS not narrowing to "string"
32
+ const editUrlOption = options.editUrl;
33
+ const getEditUrl = () => {
34
+ const editDirPath = options.editCurrentVersion ? options.path : contentPath;
35
+ return (0, utils_1.normalizeUrl)([
36
+ editUrlOption,
37
+ (0, utils_1.posixPath)(node_path_1.default.relative(context.siteDir, node_path_1.default.resolve(context.siteDir, editDirPath))),
38
+ ]);
39
+ };
40
+ const getEditUrlLocalized = () => {
41
+ if (!contentPathLocalized) {
42
+ return undefined;
43
+ }
44
+ const editDirPathLocalized = options.editCurrentVersion
45
+ ? (0, files_1.getDocsDirPathLocalized)({
46
+ localizationDir: context.localizationDir,
47
+ versionName: constants_1.CURRENT_VERSION_NAME,
48
+ pluginId: options.id,
49
+ })
50
+ : contentPathLocalized;
51
+ return (0, utils_1.normalizeUrl)([
52
+ editUrlOption,
53
+ (0, utils_1.posixPath)(node_path_1.default.relative(context.siteDir, node_path_1.default.resolve(context.siteDir, editDirPathLocalized))),
54
+ ]);
55
+ };
56
+ return { editUrl: getEditUrl(), editUrlLocalized: getEditUrlLocalized() };
57
+ }
58
+ /**
59
+ * The default version banner depends on the version's relative position to the
60
+ * latest version. More recent ones are "unreleased", and older ones are
61
+ * "unmaintained".
62
+ */
63
+ function getDefaultVersionBanner({ versionName, versionNames, lastVersionName, }) {
64
+ // Current version: good, no banner
65
+ if (versionName === lastVersionName) {
66
+ return null;
67
+ }
68
+ // Upcoming versions: unreleased banner
69
+ if (versionNames.indexOf(versionName) < versionNames.indexOf(lastVersionName)) {
70
+ return 'unreleased';
71
+ }
72
+ // Older versions: display unmaintained banner
73
+ return 'unmaintained';
74
+ }
75
+ function getVersionBanner(context) {
76
+ const { versionName, options } = context;
77
+ const versionBannerOption = options.versions[versionName]?.banner;
78
+ if (versionBannerOption) {
79
+ return versionBannerOption === 'none' ? null : versionBannerOption;
80
+ }
81
+ return getDefaultVersionBanner(context);
82
+ }
83
+ function getVersionBadge({ versionName, versionNames, options, }) {
84
+ // If site is not versioned or only one version is included
85
+ // we don't show the version badge by default
86
+ // See https://github.com/facebook/docusaurus/issues/3362
87
+ const defaultVersionBadge = versionNames.length !== 1;
88
+ return options.versions[versionName]?.badge ?? defaultVersionBadge;
89
+ }
90
+ function getVersionNoIndex({ versionName, options, }) {
91
+ return options.versions[versionName]?.noIndex ?? false;
92
+ }
93
+ function getVersionClassName({ versionName, options, }) {
94
+ const defaultVersionClassName = `docs-version-${versionName}`;
95
+ return options.versions[versionName]?.className ?? defaultVersionClassName;
96
+ }
97
+ function getVersionLabel({ versionName, options, }) {
98
+ const defaultVersionLabel = versionName === constants_1.CURRENT_VERSION_NAME ? 'Next' : versionName;
99
+ return options.versions[versionName]?.label ?? defaultVersionLabel;
100
+ }
101
+ function getVersionPathPart({ versionName, options, lastVersionName, }) {
102
+ function getDefaultVersionPathPart() {
103
+ if (versionName === lastVersionName) {
104
+ return '';
105
+ }
106
+ return versionName === constants_1.CURRENT_VERSION_NAME ? 'next' : versionName;
107
+ }
108
+ return options.versions[versionName]?.path ?? getDefaultVersionPathPart();
109
+ }
110
+ async function createVersionMetadata(context) {
111
+ const { versionName, lastVersionName, options, context: loadContext } = context;
112
+ const { sidebarFilePath, contentPath, contentPathLocalized } = await (0, files_1.getVersionMetadataPaths)(context);
113
+ const versionPathPart = getVersionPathPart(context);
114
+ const routePath = (0, utils_1.normalizeUrl)([
115
+ loadContext.baseUrl,
116
+ options.routeBasePath,
117
+ versionPathPart,
118
+ ]);
119
+ const versionEditUrls = getVersionEditUrls({
120
+ contentPath,
121
+ contentPathLocalized,
122
+ context: loadContext,
123
+ options,
124
+ });
125
+ return {
126
+ versionName,
127
+ label: getVersionLabel(context),
128
+ banner: getVersionBanner(context),
129
+ badge: getVersionBadge(context),
130
+ noIndex: getVersionNoIndex(context),
131
+ className: getVersionClassName(context),
132
+ path: routePath,
133
+ tagsPath: (0, utils_1.normalizeUrl)([routePath, options.tagsBasePath]),
134
+ ...versionEditUrls,
135
+ isLast: versionName === lastVersionName,
136
+ routePriority: versionPathPart === '' ? -1 : undefined,
137
+ sidebarFilePath,
138
+ contentPath,
139
+ contentPathLocalized,
140
+ };
141
+ }
142
+ /**
143
+ * Filter versions according to provided options (i.e. `onlyIncludeVersions`).
144
+ *
145
+ * Note: we preserve the order in which versions are provided; the order of the
146
+ * `onlyIncludeVersions` array does not matter
147
+ */
148
+ function filterVersions(versionNamesUnfiltered, options) {
149
+ if (options.onlyIncludeVersions) {
150
+ return versionNamesUnfiltered.filter((name) => options.onlyIncludeVersions.includes(name));
151
+ }
152
+ return versionNamesUnfiltered;
153
+ }
154
+ function getLastVersionName({ versionNames, options, }) {
155
+ return (options.lastVersion ??
156
+ versionNames.find((name) => name !== constants_1.CURRENT_VERSION_NAME) ??
157
+ constants_1.CURRENT_VERSION_NAME);
158
+ }
159
+ async function readVersionsMetadata({ context, options, }) {
160
+ const allVersionNames = await (0, files_1.readVersionNames)(context.siteDir, options);
161
+ (0, validation_1.validateVersionsOptions)(allVersionNames, options);
162
+ const versionNames = filterVersions(allVersionNames, options);
163
+ const lastVersionName = getLastVersionName({ versionNames, options });
164
+ return Promise.all(versionNames.map((versionName) => createVersionMetadata({
165
+ versionName,
166
+ versionNames,
167
+ lastVersionName,
168
+ context,
169
+ options,
170
+ })));
171
+ }
172
+ function toFullVersion(version) {
173
+ const sidebarsUtils = (0, utils_2.createSidebarsUtils)(version.sidebars);
174
+ return {
175
+ ...version,
176
+ sidebarsUtils,
177
+ categoryGeneratedIndices: (0, categoryGeneratedIndex_1.getCategoryGeneratedIndexMetadataList)({
178
+ docs: version.docs,
179
+ sidebarsUtils,
180
+ }),
181
+ };
182
+ }
183
+ function getVersionFromSourceFilePath(filePath, versionsMetadata) {
184
+ const versionFound = versionsMetadata.find((version) => (0, utils_1.getContentPathList)(version).some((docsDirPath) => filePath.startsWith(docsDirPath)));
185
+ if (!versionFound) {
186
+ throw new Error(`Unexpected error: file at "${filePath}" does not belong to any docs version!`);
187
+ }
188
+ return versionFound;
189
+ }
package/package.json CHANGED
@@ -1,6 +1,72 @@
1
1
  {
2
2
  "name": "@azerion/bunosaurus-plugin-content-docs",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "4.0.0",
4
+ "description": "Docs plugin for Docusaurus.",
5
+ "main": "lib/index.js",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ "./lib/*": "./lib/*",
9
+ "./src/*": "./src/*",
10
+ "./client": {
11
+ "type": "./lib/client/index.d.ts",
12
+ "default": "./lib/client/index.js"
13
+ },
14
+ "./server": {
15
+ "type": "./lib/server-export.d.ts",
16
+ "default": "./lib/server-export.js"
17
+ },
18
+ ".": {
19
+ "types": "./src/plugin-content-docs.d.ts",
20
+ "default": "./lib/index.js"
21
+ }
22
+ },
23
+ "types": "src/plugin-content-docs.d.ts",
24
+ "scripts": {
25
+ "build": "tsc --build",
26
+ "watch": "tsc --build --watch"
27
+ },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/azerion/bunosaurus.git",
34
+ "directory": "packages/docusaurus-plugin-content-docs"
35
+ },
36
+ "license": "MIT",
37
+ "dependencies": {
38
+ "@docusaurus/core": "npm:@azerion/bunosaurus-core@4.0.0",
39
+ "@docusaurus/fs": "npm:@azerion/bunosaurus-fs@4.0.0",
40
+ "@docusaurus/glob": "npm:@azerion/bunosaurus-glob@4.0.0",
41
+ "@docusaurus/logger": "npm:@azerion/bunosaurus-logger@4.0.0",
42
+ "@docusaurus/mdx-loader": "npm:@azerion/bunosaurus-mdx-loader@4.0.0",
43
+ "@docusaurus/module-type-aliases": "npm:@azerion/bunosaurus-module-type-aliases@4.0.0",
44
+ "@docusaurus/theme-common": "npm:@azerion/bunosaurus-theme-common@4.0.0",
45
+ "@docusaurus/types": "npm:@azerion/bunosaurus-types@4.0.0",
46
+ "@docusaurus/utils": "npm:@azerion/bunosaurus-utils@4.0.0",
47
+ "@docusaurus/utils-common": "npm:@azerion/bunosaurus-utils-common@4.0.0",
48
+ "@docusaurus/utils-validation": "npm:@azerion/bunosaurus-utils-validation@4.0.0",
49
+ "@types/react-router-config": "^5.0.11",
50
+ "combine-promises": "^1.2.0",
51
+ "js-yaml": "^5.4.1",
52
+ "lodash": "^4.18.1",
53
+ "schema-dts": "^1.1.5",
54
+ "tslib": "^2.8.1",
55
+ "utility-types": "^3.11.0"
56
+ },
57
+ "devDependencies": {
58
+ "@total-typescript/shoehorn": "^0.1.2",
59
+ "@types/picomatch": "^2.3.4",
60
+ "commander": "^15.0.0",
61
+ "picomatch": "^2.3.2",
62
+ "react-router-dom": "^5.3.4",
63
+ "webpack": "^5.111.0"
64
+ },
65
+ "peerDependencies": {
66
+ "react": "^19.3.0",
67
+ "react-dom": "^19.3.0"
68
+ },
69
+ "engines": {
70
+ "bun": ">=1.4.1"
71
+ }
72
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import {type SidebarsUtils, toNavigationLink} from './sidebars/utils';
9
+ import {createDocsByIdIndex} from './docs';
10
+ import type {
11
+ CategoryGeneratedIndexMetadata,
12
+ DocMetadataBase,
13
+ } from '@docusaurus/plugin-content-docs';
14
+ import type {SidebarItemCategoryWithGeneratedIndex} from './sidebars/types';
15
+
16
+ function getCategoryGeneratedIndexMetadata({
17
+ category,
18
+ sidebarsUtils,
19
+ docsById,
20
+ }: {
21
+ category: SidebarItemCategoryWithGeneratedIndex;
22
+ sidebarsUtils: SidebarsUtils;
23
+ docsById: {[docId: string]: DocMetadataBase};
24
+ }): CategoryGeneratedIndexMetadata {
25
+ const {sidebarName, previous, next} =
26
+ sidebarsUtils.getCategoryGeneratedIndexNavigation(category.link.permalink);
27
+ return {
28
+ title: category.link.title ?? category.label,
29
+ description: category.link.description,
30
+ image: category.link.image,
31
+ keywords: category.link.keywords,
32
+ slug: category.link.slug,
33
+ permalink: category.link.permalink,
34
+ sidebar: sidebarName!,
35
+ navigation: {
36
+ previous: toNavigationLink(previous, docsById),
37
+ next: toNavigationLink(next, docsById),
38
+ },
39
+ };
40
+ }
41
+
42
+ export function getCategoryGeneratedIndexMetadataList({
43
+ docs,
44
+ sidebarsUtils,
45
+ }: {
46
+ sidebarsUtils: SidebarsUtils;
47
+ docs: DocMetadataBase[];
48
+ }): CategoryGeneratedIndexMetadata[] {
49
+ const docsById = createDocsByIdIndex(docs);
50
+
51
+ const categoryGeneratedIndexItems =
52
+ sidebarsUtils.getCategoryGeneratedIndexList();
53
+ return categoryGeneratedIndexItems.map((category) =>
54
+ getCategoryGeneratedIndexMetadata({
55
+ category,
56
+ sidebarsUtils,
57
+ docsById,
58
+ }),
59
+ );
60
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import path from 'node:path';
9
+ import logger from '@docusaurus/logger';
10
+ import {DEFAULT_PLUGIN_ID, getLocaleConfig} from '@docusaurus/utils';
11
+ import {fs, pathExists, outputFile} from '@docusaurus/fs';
12
+ import {
13
+ getVersionsFilePath,
14
+ getVersionDocsDirPath,
15
+ getVersionSidebarsPath,
16
+ getDocsDirPathLocalized,
17
+ getPluginDirPathLocalized,
18
+ readVersionsFile,
19
+ } from './versions/files';
20
+ import {validateVersionName} from './versions/validation';
21
+ import {loadSidebarsFile} from './sidebars';
22
+ import {CURRENT_VERSION_NAME} from './constants';
23
+ import type {PluginOptions} from '@docusaurus/plugin-content-docs';
24
+ import type {LoadContext} from '@docusaurus/types';
25
+
26
+ async function createVersionedSidebarFile({
27
+ siteDir,
28
+ pluginId,
29
+ sidebarPath,
30
+ version,
31
+ }: {
32
+ siteDir: string;
33
+ pluginId: string;
34
+ sidebarPath: string | false | undefined;
35
+ version: string;
36
+ }) {
37
+ // Load current sidebar and create a new versioned sidebars file (if needed).
38
+ // Note: we don't need the sidebars file to be normalized: it's ok to let
39
+ // plugin option changes to impact older, versioned sidebars
40
+ // We don't validate here, assuming the user has already built the version
41
+ const sidebars = await loadSidebarsFile(sidebarPath);
42
+
43
+ // Do not create a useless versioned sidebars file if sidebars file is empty
44
+ // or sidebars are disabled/false)
45
+ const shouldCreateVersionedSidebarFile = Object.keys(sidebars).length > 0;
46
+
47
+ if (shouldCreateVersionedSidebarFile) {
48
+ await outputFile(
49
+ getVersionSidebarsPath(siteDir, pluginId, version),
50
+ `${JSON.stringify(sidebars, null, 2)}\n`,
51
+ 'utf8',
52
+ );
53
+ }
54
+ }
55
+
56
+ // Tests depend on non-default export for mocking.
57
+ async function cliDocsVersionCommand(
58
+ version: unknown,
59
+ {id: pluginId, path: docsPath, sidebarPath}: PluginOptions,
60
+ {siteDir, i18n}: LoadContext,
61
+ ): Promise<void> {
62
+ // It wouldn't be very user-friendly to show a [default] log prefix,
63
+ // so we use [docs] instead of [default]
64
+ const pluginIdLogPrefix =
65
+ pluginId === DEFAULT_PLUGIN_ID ? '[docs]' : `[${pluginId}]`;
66
+
67
+ try {
68
+ validateVersionName(version);
69
+ } catch (err) {
70
+ logger.info`${pluginIdLogPrefix}: Invalid version name provided. Try something like: 1.0.0`;
71
+ throw err;
72
+ }
73
+
74
+ const versions = (await readVersionsFile(siteDir, pluginId)) ?? [];
75
+
76
+ // Check if version already exists.
77
+ if (versions.includes(version)) {
78
+ throw new Error(
79
+ `${pluginIdLogPrefix}: this version already exists! Use a version tag that does not already exist.`,
80
+ );
81
+ }
82
+
83
+ if (i18n.locales.length > 1) {
84
+ logger.info`Versioned docs will be created for the following locales: name=${i18n.locales}`;
85
+ }
86
+
87
+ await Promise.all(
88
+ i18n.locales.map(async (locale) => {
89
+ const localizationDir = path.resolve(
90
+ siteDir,
91
+ i18n.path,
92
+ getLocaleConfig(i18n, locale).path,
93
+ );
94
+ // Copy docs files.
95
+ const docsDir =
96
+ locale === i18n.defaultLocale
97
+ ? path.resolve(siteDir, docsPath)
98
+ : getDocsDirPathLocalized({
99
+ localizationDir,
100
+ pluginId,
101
+ versionName: CURRENT_VERSION_NAME,
102
+ });
103
+
104
+ if (
105
+ !(await pathExists(docsDir)) ||
106
+ (await fs.readdir(docsDir)).length === 0
107
+ ) {
108
+ if (locale === i18n.defaultLocale) {
109
+ throw new Error(
110
+ logger.interpolate`${pluginIdLogPrefix}: no docs found in path=${docsDir}.`,
111
+ );
112
+ } else {
113
+ logger.warn`${pluginIdLogPrefix}: no docs found in path=${docsDir}. Skipping.`;
114
+ return;
115
+ }
116
+ }
117
+
118
+ const newVersionDir =
119
+ locale === i18n.defaultLocale
120
+ ? getVersionDocsDirPath(siteDir, pluginId, version)
121
+ : getDocsDirPathLocalized({
122
+ localizationDir,
123
+ pluginId,
124
+ versionName: version,
125
+ });
126
+ // verbatimSymlinks: keep relative symlinks relative
127
+ await fs.cp(docsDir, newVersionDir, {
128
+ recursive: true,
129
+ verbatimSymlinks: true,
130
+ });
131
+
132
+ // Copy version JSON translation file for this locale
133
+ // i18n/<l>/docusaurus-plugin-content-docs/current.json => version-v1.json
134
+ // See https://docusaurus.io/docs/next/api/plugins/@docusaurus/plugin-content-docs#translation-files-location
135
+ if (locale !== i18n.defaultLocale) {
136
+ const dir = getPluginDirPathLocalized({
137
+ localizationDir,
138
+ pluginId,
139
+ });
140
+ const sourceFile = path.join(dir, 'current.json');
141
+ const dest = path.join(dir, `version-${version}.json`);
142
+ if (await pathExists(sourceFile)) {
143
+ await fs.cp(sourceFile, dest, {verbatimSymlinks: true});
144
+ } else {
145
+ logger.warn`${pluginIdLogPrefix}: i18n translation file does not exist in path=${sourceFile}. Skipping.`;
146
+ }
147
+ }
148
+ }),
149
+ );
150
+
151
+ await createVersionedSidebarFile({
152
+ siteDir,
153
+ pluginId,
154
+ version,
155
+ sidebarPath,
156
+ });
157
+
158
+ // Update versions.json file.
159
+ versions.unshift(version);
160
+ await outputFile(
161
+ getVersionsFilePath(siteDir, pluginId),
162
+ `${JSON.stringify(versions, null, 2)}\n`,
163
+ );
164
+
165
+ logger.success`name=${pluginIdLogPrefix}: version name=${version} created!`;
166
+ }
167
+
168
+ // TODO try to remove this workaround
169
+ // Why use a default export instead of named exports here?
170
+ // This is only to make Jest mocking happy
171
+ // After upgrading Jest/SWC we got this weird mocking error in extendCli tests
172
+ // "spyOn: Cannot redefine property cliDocsVersionCommand"
173
+ // I tried various workarounds, and it's the only one that worked :/
174
+ // See also:
175
+ // - https://pyk.sh/fixing-typeerror-cannot-redefine-property-x-error-in-jest-tests#heading-solution-2-using-barrel-imports
176
+ // - https://github.com/aelbore/esbuild-jest/issues/26
177
+ // - https://stackoverflow.com/questions/67872622/jest-spyon-not-working-on-index-file-cannot-redefine-property/69951703#69951703
178
+ export default {
179
+ cliDocsVersionCommand,
180
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ import React, {useMemo, type ReactNode, useContext} from 'react';
9
+ import {ReactContextError} from '@docusaurus/theme-common';
10
+ import type {PropDocContent} from '@docusaurus/plugin-content-docs';
11
+
12
+ /**
13
+ * The React context value returned by the `useDoc()` hook.
14
+ * It contains useful data related to the currently browsed doc.
15
+ */
16
+ export type DocContextValue = Pick<
17
+ PropDocContent,
18
+ 'metadata' | 'frontMatter' | 'toc' | 'assets' | 'contentTitle'
19
+ >;
20
+
21
+ const Context = React.createContext<DocContextValue | null>(null);
22
+
23
+ /**
24
+ * Note: we don't use `PropDoc` as context value on purpose. Metadata is
25
+ * currently stored inside the MDX component, but we may want to change that in
26
+ * the future. This layer is a good opportunity to decouple storage from
27
+ * consuming API, potentially allowing us to provide metadata as both props and
28
+ * route context without duplicating the chunks in the future.
29
+ */
30
+ function useContextValue(content: PropDocContent): DocContextValue {
31
+ return useMemo(
32
+ () => ({
33
+ metadata: content.metadata,
34
+ frontMatter: content.frontMatter,
35
+ assets: content.assets,
36
+ contentTitle: content.contentTitle,
37
+ toc: content.toc,
38
+ }),
39
+ [content],
40
+ );
41
+ }
42
+
43
+ /**
44
+ * This is a very thin layer around the `content` received from the MDX loader.
45
+ * It provides metadata about the doc to the children tree.
46
+ */
47
+ export function DocProvider({
48
+ children,
49
+ content,
50
+ }: {
51
+ children: ReactNode;
52
+ content: PropDocContent;
53
+ }): ReactNode {
54
+ const contextValue = useContextValue(content);
55
+ return <Context value={contextValue}>{children}</Context>;
56
+ }
57
+
58
+ /**
59
+ * Returns the data of the currently browsed doc. Gives access to the doc's MDX
60
+ * Component, front matter, metadata, TOC, etc. When swizzling a low-level
61
+ * component (e.g. the "Edit this page" link) and you need some extra metadata,
62
+ * you don't have to drill the props all the way through the component tree:
63
+ * simply use this hook instead.
64
+ */
65
+ export function useDoc(): DocContextValue {
66
+ const doc = useContext(Context);
67
+ if (doc === null) {
68
+ throw new ReactContextError('DocProvider');
69
+ }
70
+ return doc;
71
+ }