@docusaurus/plugin-content-docs 2.0.0-beta.18 → 2.0.0-beta.19
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/categoryGeneratedIndex.d.ts +1 -1
- package/lib/categoryGeneratedIndex.js +4 -2
- package/lib/cli.d.ts +3 -2
- package/lib/cli.js +45 -35
- package/lib/client/docsClientUtils.d.ts +6 -5
- package/lib/client/docsClientUtils.js +8 -10
- package/lib/client/index.d.ts +9 -8
- package/lib/client/index.js +30 -33
- package/lib/constants.d.ts +4 -0
- package/lib/constants.js +4 -1
- package/lib/docs.d.ts +5 -3
- package/lib/docs.js +18 -13
- package/lib/frontMatter.d.ts +1 -1
- package/lib/frontMatter.js +3 -0
- package/lib/globalData.d.ts +2 -2
- package/lib/globalData.js +6 -6
- package/lib/index.d.ts +1 -2
- package/lib/index.js +31 -23
- package/lib/markdown/linkify.js +1 -2
- package/lib/props.d.ts +3 -3
- package/lib/props.js +7 -6
- package/lib/routes.d.ts +5 -4
- package/lib/routes.js +9 -23
- package/lib/server-export.d.ts +2 -1
- package/lib/server-export.js +3 -4
- package/lib/sidebars/generator.js +3 -3
- package/lib/sidebars/index.js +0 -2
- package/lib/sidebars/normalization.js +1 -1
- package/lib/sidebars/postProcessor.js +2 -2
- package/lib/sidebars/processor.js +14 -3
- package/lib/sidebars/types.d.ts +18 -14
- package/lib/sidebars/utils.d.ts +3 -3
- package/lib/slug.d.ts +1 -2
- package/lib/tags.d.ts +2 -1
- package/lib/tags.js +1 -1
- package/lib/translations.d.ts +3 -3
- package/lib/translations.js +3 -53
- package/lib/types.d.ts +8 -96
- package/lib/versions/files.d.ts +44 -0
- package/lib/versions/files.js +142 -0
- package/lib/versions/index.d.ts +36 -0
- package/lib/versions/index.js +155 -0
- package/lib/versions/validation.d.ts +17 -0
- package/lib/versions/validation.js +71 -0
- package/package.json +14 -12
- package/src/categoryGeneratedIndex.ts +8 -3
- package/src/cli.ts +62 -65
- package/src/client/docsClientUtils.ts +11 -13
- package/src/client/index.ts +41 -42
- package/src/constants.ts +4 -2
- package/src/docs.ts +41 -26
- package/src/frontMatter.ts +6 -3
- package/src/globalData.ts +10 -10
- package/src/index.ts +44 -38
- package/src/markdown/linkify.ts +2 -3
- package/src/plugin-content-docs.d.ts +447 -113
- package/src/props.ts +12 -9
- package/src/routes.ts +13 -39
- package/src/server-export.ts +1 -3
- package/src/sidebars/generator.ts +4 -4
- package/src/sidebars/index.ts +0 -2
- package/src/sidebars/normalization.ts +1 -1
- package/src/sidebars/postProcessor.ts +2 -2
- package/src/sidebars/processor.ts +24 -5
- package/src/sidebars/types.ts +20 -19
- package/src/sidebars/utils.ts +6 -3
- package/src/slug.ts +4 -2
- package/src/tags.ts +3 -2
- package/src/translations.ts +10 -61
- package/src/types.ts +13 -106
- package/src/versions/files.ts +220 -0
- package/src/versions/index.ts +247 -0
- package/src/versions/validation.ts +113 -0
- package/lib/versions.d.ts +0 -41
- package/lib/versions.js +0 -324
- package/src/versions.ts +0 -606
package/src/cli.ts
CHANGED
|
@@ -7,18 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
getVersionsFilePath,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
getVersionDocsDirPath,
|
|
11
|
+
getVersionSidebarsPath,
|
|
12
|
+
getDocsDirPathLocalized,
|
|
13
|
+
} from './versions/files';
|
|
14
|
+
import {validateVersionName} from './versions/validation';
|
|
13
15
|
import fs from 'fs-extra';
|
|
14
16
|
import path from 'path';
|
|
15
|
-
import type {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
} from '@docusaurus/plugin-content-docs';
|
|
19
|
-
import {loadSidebarsFileUnsafe, resolveSidebarPathOption} from './sidebars';
|
|
17
|
+
import type {PluginOptions} from '@docusaurus/plugin-content-docs';
|
|
18
|
+
import {loadSidebarsFileUnsafe} from './sidebars';
|
|
19
|
+
import {CURRENT_VERSION_NAME} from './constants';
|
|
20
20
|
import {DEFAULT_PLUGIN_ID} from '@docusaurus/utils';
|
|
21
21
|
import logger from '@docusaurus/logger';
|
|
22
|
+
import type {LoadContext} from '@docusaurus/types';
|
|
22
23
|
|
|
23
24
|
async function createVersionedSidebarFile({
|
|
24
25
|
siteDir,
|
|
@@ -42,13 +43,8 @@ async function createVersionedSidebarFile({
|
|
|
42
43
|
const shouldCreateVersionedSidebarFile = Object.keys(sidebars).length > 0;
|
|
43
44
|
|
|
44
45
|
if (shouldCreateVersionedSidebarFile) {
|
|
45
|
-
const versionedSidebarsDir = getVersionedSidebarsDirPath(siteDir, pluginId);
|
|
46
|
-
const newSidebarFile = path.join(
|
|
47
|
-
versionedSidebarsDir,
|
|
48
|
-
`version-${version}-sidebars.json`,
|
|
49
|
-
);
|
|
50
46
|
await fs.outputFile(
|
|
51
|
-
|
|
47
|
+
getVersionSidebarsPath(siteDir, pluginId, version),
|
|
52
48
|
`${JSON.stringify(sidebars, null, 2)}\n`,
|
|
53
49
|
'utf8',
|
|
54
50
|
);
|
|
@@ -57,54 +53,27 @@ async function createVersionedSidebarFile({
|
|
|
57
53
|
|
|
58
54
|
// Tests depend on non-default export for mocking.
|
|
59
55
|
export async function cliDocsVersionCommand(
|
|
60
|
-
version: string
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
options: PathOptions & SidebarOptions,
|
|
56
|
+
version: string,
|
|
57
|
+
{id: pluginId, path: docsPath, sidebarPath}: PluginOptions,
|
|
58
|
+
{siteDir, i18n}: LoadContext,
|
|
64
59
|
): Promise<void> {
|
|
65
60
|
// It wouldn't be very user-friendly to show a [default] log prefix,
|
|
66
61
|
// so we use [docs] instead of [default]
|
|
67
62
|
const pluginIdLogPrefix =
|
|
68
63
|
pluginId === DEFAULT_PLUGIN_ID ? '[docs]' : `[${pluginId}]`;
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (version.includes('/') || version.includes('\\')) {
|
|
77
|
-
throw new Error(
|
|
78
|
-
`${pluginIdLogPrefix}: invalid version tag specified! Do not include slash (/) or backslash (\\). Try something like: 1.0.0.`,
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (version.length > 32) {
|
|
83
|
-
throw new Error(
|
|
84
|
-
`${pluginIdLogPrefix}: invalid version tag specified! Length cannot exceed 32 characters. Try something like: 1.0.0.`,
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// Since we are going to create `version-${version}` folder, we need to make
|
|
89
|
-
// sure it's a valid pathname.
|
|
90
|
-
// eslint-disable-next-line no-control-regex
|
|
91
|
-
if (/[<>:"|?*\x00-\x1F]/.test(version)) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`${pluginIdLogPrefix}: invalid version tag specified! Please ensure its a valid pathname too. Try something like: 1.0.0.`,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (/^\.\.?$/.test(version)) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`${pluginIdLogPrefix}: invalid version tag specified! Do not name your version "." or "..". Try something like: 1.0.0.`,
|
|
100
|
-
);
|
|
65
|
+
try {
|
|
66
|
+
validateVersionName(version);
|
|
67
|
+
} catch (e) {
|
|
68
|
+
logger.info`${pluginIdLogPrefix}: Invalid version name provided. Try something like: 1.0.0`;
|
|
69
|
+
throw e;
|
|
101
70
|
}
|
|
102
71
|
|
|
103
72
|
// Load existing versions.
|
|
104
73
|
let versions = [];
|
|
105
74
|
const versionsJSONFile = getVersionsFilePath(siteDir, pluginId);
|
|
106
75
|
if (await fs.pathExists(versionsJSONFile)) {
|
|
107
|
-
versions =
|
|
76
|
+
versions = await fs.readJSON(versionsJSONFile);
|
|
108
77
|
}
|
|
109
78
|
|
|
110
79
|
// Check if version already exists.
|
|
@@ -114,27 +83,55 @@ export async function cliDocsVersionCommand(
|
|
|
114
83
|
);
|
|
115
84
|
}
|
|
116
85
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// Copy docs files.
|
|
120
|
-
const docsDir = path.join(siteDir, docsPath);
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
(await fs.pathExists(docsDir)) &&
|
|
124
|
-
(await fs.readdir(docsDir)).length > 0
|
|
125
|
-
) {
|
|
126
|
-
const versionedDir = getVersionedDocsDirPath(siteDir, pluginId);
|
|
127
|
-
const newVersionDir = path.join(versionedDir, `version-${version}`);
|
|
128
|
-
await fs.copy(docsDir, newVersionDir);
|
|
129
|
-
} else {
|
|
130
|
-
throw new Error(`${pluginIdLogPrefix}: there is no docs to version!`);
|
|
86
|
+
if (i18n.locales.length > 1) {
|
|
87
|
+
logger.info`Versioned docs will be created for the following locales: name=${i18n.locales}`;
|
|
131
88
|
}
|
|
132
89
|
|
|
90
|
+
await Promise.all(
|
|
91
|
+
i18n.locales.map(async (locale) => {
|
|
92
|
+
// Copy docs files.
|
|
93
|
+
const docsDir =
|
|
94
|
+
locale === i18n.defaultLocale
|
|
95
|
+
? path.resolve(siteDir, docsPath)
|
|
96
|
+
: getDocsDirPathLocalized({
|
|
97
|
+
siteDir,
|
|
98
|
+
locale,
|
|
99
|
+
pluginId,
|
|
100
|
+
versionName: CURRENT_VERSION_NAME,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
!(await fs.pathExists(docsDir)) ||
|
|
105
|
+
(await fs.readdir(docsDir)).length === 0
|
|
106
|
+
) {
|
|
107
|
+
if (locale === i18n.defaultLocale) {
|
|
108
|
+
throw new Error(
|
|
109
|
+
logger.interpolate`${pluginIdLogPrefix}: no docs found in path=${docsDir}.`,
|
|
110
|
+
);
|
|
111
|
+
} else {
|
|
112
|
+
logger.warn`${pluginIdLogPrefix}: no docs found in path=${docsDir}. Skipping.`;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const newVersionDir =
|
|
118
|
+
locale === i18n.defaultLocale
|
|
119
|
+
? getVersionDocsDirPath(siteDir, pluginId, version)
|
|
120
|
+
: getDocsDirPathLocalized({
|
|
121
|
+
siteDir,
|
|
122
|
+
locale,
|
|
123
|
+
pluginId,
|
|
124
|
+
versionName: version,
|
|
125
|
+
});
|
|
126
|
+
await fs.copy(docsDir, newVersionDir);
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
|
|
133
130
|
await createVersionedSidebarFile({
|
|
134
131
|
siteDir,
|
|
135
132
|
pluginId,
|
|
136
133
|
version,
|
|
137
|
-
sidebarPath
|
|
134
|
+
sidebarPath,
|
|
138
135
|
});
|
|
139
136
|
|
|
140
137
|
// Update versions.json file.
|
|
@@ -11,11 +11,11 @@ import type {
|
|
|
11
11
|
GlobalPluginData,
|
|
12
12
|
GlobalVersion,
|
|
13
13
|
GlobalDoc,
|
|
14
|
-
GetActivePluginOptions,
|
|
15
14
|
ActivePlugin,
|
|
16
15
|
ActiveDocContext,
|
|
17
16
|
DocVersionSuggestions,
|
|
18
17
|
} from '@docusaurus/plugin-content-docs/client';
|
|
18
|
+
import type {UseDataOptions} from '@docusaurus/types';
|
|
19
19
|
|
|
20
20
|
// This code is not part of the api surface, not in ./theme on purpose
|
|
21
21
|
|
|
@@ -25,7 +25,7 @@ import type {
|
|
|
25
25
|
export function getActivePlugin(
|
|
26
26
|
allPluginData: {[pluginId: string]: GlobalPluginData},
|
|
27
27
|
pathname: string,
|
|
28
|
-
options:
|
|
28
|
+
options: UseDataOptions = {},
|
|
29
29
|
): ActivePlugin | undefined {
|
|
30
30
|
const activeEntry = Object.entries(allPluginData)
|
|
31
31
|
// Route sorting: '/android/foo' should match '/android' instead of '/'
|
|
@@ -59,12 +59,10 @@ export function getActivePlugin(
|
|
|
59
59
|
export const getLatestVersion = (data: GlobalPluginData): GlobalVersion =>
|
|
60
60
|
data.versions.find((version) => version.isLast)!;
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
// because there's no version currently considered as active
|
|
64
|
-
export const getActiveVersion = (
|
|
62
|
+
export function getActiveVersion(
|
|
65
63
|
data: GlobalPluginData,
|
|
66
64
|
pathname: string,
|
|
67
|
-
): GlobalVersion | undefined
|
|
65
|
+
): GlobalVersion | undefined {
|
|
68
66
|
const lastVersion = getLatestVersion(data);
|
|
69
67
|
// Last version is a route like /docs/*,
|
|
70
68
|
// we need to match it last or it would match /docs/version-1.0/* as well
|
|
@@ -80,12 +78,12 @@ export const getActiveVersion = (
|
|
|
80
78
|
strict: false,
|
|
81
79
|
}),
|
|
82
80
|
);
|
|
83
|
-
}
|
|
81
|
+
}
|
|
84
82
|
|
|
85
|
-
export
|
|
83
|
+
export function getActiveDocContext(
|
|
86
84
|
data: GlobalPluginData,
|
|
87
85
|
pathname: string,
|
|
88
|
-
): ActiveDocContext
|
|
86
|
+
): ActiveDocContext {
|
|
89
87
|
const activeVersion = getActiveVersion(data, pathname);
|
|
90
88
|
const activeDoc = activeVersion?.docs.find(
|
|
91
89
|
(doc) =>
|
|
@@ -119,15 +117,15 @@ export const getActiveDocContext = (
|
|
|
119
117
|
activeDoc,
|
|
120
118
|
alternateDocVersions: alternateVersionDocs,
|
|
121
119
|
};
|
|
122
|
-
}
|
|
120
|
+
}
|
|
123
121
|
|
|
124
|
-
export
|
|
122
|
+
export function getDocVersionSuggestions(
|
|
125
123
|
data: GlobalPluginData,
|
|
126
124
|
pathname: string,
|
|
127
|
-
): DocVersionSuggestions
|
|
125
|
+
): DocVersionSuggestions {
|
|
128
126
|
const latestVersion = getLatestVersion(data);
|
|
129
127
|
const activeDocContext = getActiveDocContext(data, pathname);
|
|
130
128
|
const latestDocSuggestion: GlobalDoc | undefined =
|
|
131
129
|
activeDocContext?.alternateDocVersions[latestVersion.name];
|
|
132
130
|
return {latestDocSuggestion, latestVersionSuggestion: latestVersion};
|
|
133
|
-
}
|
|
131
|
+
}
|
package/src/client/index.ts
CHANGED
|
@@ -6,7 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import {useLocation} from '@docusaurus/router';
|
|
9
|
-
import
|
|
9
|
+
import {
|
|
10
|
+
useAllPluginInstancesData,
|
|
11
|
+
usePluginData,
|
|
12
|
+
} from '@docusaurus/useGlobalData';
|
|
10
13
|
|
|
11
14
|
import {
|
|
12
15
|
getActivePlugin,
|
|
@@ -21,84 +24,80 @@ import type {
|
|
|
21
24
|
ActivePlugin,
|
|
22
25
|
ActiveDocContext,
|
|
23
26
|
DocVersionSuggestions,
|
|
24
|
-
GetActivePluginOptions,
|
|
25
27
|
} from '@docusaurus/plugin-content-docs/client';
|
|
28
|
+
import type {UseDataOptions} from '@docusaurus/types';
|
|
26
29
|
|
|
27
30
|
// Important to use a constant object to avoid React useEffect executions etc.
|
|
28
31
|
// see https://github.com/facebook/docusaurus/issues/5089
|
|
29
32
|
const StableEmptyObject = {};
|
|
30
33
|
|
|
31
|
-
//
|
|
32
|
-
//
|
|
33
|
-
// plugin is not in use
|
|
34
|
+
// In blog-only mode, docs hooks are still used by the theme. We need a fail-
|
|
35
|
+
// safe fallback when the docs plugin is not in use
|
|
34
36
|
export const useAllDocsData = (): {[pluginId: string]: GlobalPluginData} =>
|
|
35
|
-
|
|
37
|
+
useAllPluginInstancesData('docusaurus-plugin-content-docs') ??
|
|
38
|
+
StableEmptyObject;
|
|
36
39
|
|
|
37
40
|
export const useDocsData = (pluginId: string | undefined): GlobalPluginData =>
|
|
38
|
-
usePluginData('docusaurus-plugin-content-docs', pluginId
|
|
41
|
+
usePluginData('docusaurus-plugin-content-docs', pluginId, {
|
|
42
|
+
failfast: true,
|
|
43
|
+
}) as GlobalPluginData;
|
|
39
44
|
|
|
40
45
|
// TODO this feature should be provided by docusaurus core
|
|
41
|
-
export
|
|
42
|
-
options:
|
|
43
|
-
): ActivePlugin | undefined
|
|
46
|
+
export function useActivePlugin(
|
|
47
|
+
options: UseDataOptions = {},
|
|
48
|
+
): ActivePlugin | undefined {
|
|
44
49
|
const data = useAllDocsData();
|
|
45
50
|
const {pathname} = useLocation();
|
|
46
51
|
return getActivePlugin(data, pathname, options);
|
|
47
|
-
}
|
|
52
|
+
}
|
|
48
53
|
|
|
49
|
-
export
|
|
50
|
-
options:
|
|
54
|
+
export function useActivePluginAndVersion(
|
|
55
|
+
options: UseDataOptions = {},
|
|
51
56
|
):
|
|
52
|
-
| undefined
|
|
53
|
-
|
|
|
57
|
+
| {activePlugin: ActivePlugin; activeVersion: GlobalVersion | undefined}
|
|
58
|
+
| undefined {
|
|
54
59
|
const activePlugin = useActivePlugin(options);
|
|
55
60
|
const {pathname} = useLocation();
|
|
56
|
-
if (activePlugin) {
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
activePlugin,
|
|
60
|
-
activeVersion,
|
|
61
|
-
};
|
|
61
|
+
if (!activePlugin) {
|
|
62
|
+
return undefined;
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
const activeVersion = getActiveVersion(activePlugin.pluginData, pathname);
|
|
65
|
+
return {
|
|
66
|
+
activePlugin,
|
|
67
|
+
activeVersion,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
65
70
|
|
|
66
|
-
|
|
67
|
-
export const useVersions = (pluginId: string | undefined): GlobalVersion[] => {
|
|
71
|
+
export function useVersions(pluginId: string | undefined): GlobalVersion[] {
|
|
68
72
|
const data = useDocsData(pluginId);
|
|
69
73
|
return data.versions;
|
|
70
|
-
}
|
|
74
|
+
}
|
|
71
75
|
|
|
72
|
-
export
|
|
73
|
-
pluginId: string | undefined,
|
|
74
|
-
): GlobalVersion => {
|
|
76
|
+
export function useLatestVersion(pluginId: string | undefined): GlobalVersion {
|
|
75
77
|
const data = useDocsData(pluginId);
|
|
76
78
|
return getLatestVersion(data);
|
|
77
|
-
}
|
|
79
|
+
}
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
// because there's no version currently considered as active
|
|
81
|
-
export const useActiveVersion = (
|
|
81
|
+
export function useActiveVersion(
|
|
82
82
|
pluginId: string | undefined,
|
|
83
|
-
): GlobalVersion | undefined
|
|
83
|
+
): GlobalVersion | undefined {
|
|
84
84
|
const data = useDocsData(pluginId);
|
|
85
85
|
const {pathname} = useLocation();
|
|
86
86
|
return getActiveVersion(data, pathname);
|
|
87
|
-
}
|
|
87
|
+
}
|
|
88
88
|
|
|
89
|
-
export
|
|
89
|
+
export function useActiveDocContext(
|
|
90
90
|
pluginId: string | undefined,
|
|
91
|
-
): ActiveDocContext
|
|
91
|
+
): ActiveDocContext {
|
|
92
92
|
const data = useDocsData(pluginId);
|
|
93
93
|
const {pathname} = useLocation();
|
|
94
94
|
return getActiveDocContext(data, pathname);
|
|
95
|
-
}
|
|
95
|
+
}
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
export const useDocVersionSuggestions = (
|
|
97
|
+
export function useDocVersionSuggestions(
|
|
99
98
|
pluginId: string | undefined,
|
|
100
|
-
): DocVersionSuggestions
|
|
99
|
+
): DocVersionSuggestions {
|
|
101
100
|
const data = useDocsData(pluginId);
|
|
102
101
|
const {pathname} = useLocation();
|
|
103
102
|
return getDocVersionSuggestions(data, pathname);
|
|
104
|
-
}
|
|
103
|
+
}
|
package/src/constants.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/** The name of the version that's actively worked on (e.g. `website/docs`) */
|
|
9
9
|
export const CURRENT_VERSION_NAME = 'current';
|
|
10
|
-
|
|
10
|
+
/** All doc versions are stored here by version names */
|
|
11
11
|
export const VERSIONED_DOCS_DIR = 'versioned_docs';
|
|
12
|
+
/** All doc versioned sidebars are stored here by version names */
|
|
12
13
|
export const VERSIONED_SIDEBARS_DIR = 'versioned_sidebars';
|
|
14
|
+
/** The version names. Should 1-1 map to the content of versioned docs dir. */
|
|
13
15
|
export const VERSIONS_JSON_FILE = 'versions.json';
|
package/src/docs.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
aliasedSitePath,
|
|
13
13
|
getEditUrl,
|
|
14
14
|
getFolderContainingFile,
|
|
15
|
+
getContentPathList,
|
|
15
16
|
normalizeUrl,
|
|
16
17
|
parseMarkdownString,
|
|
17
18
|
posixPath,
|
|
@@ -21,18 +22,9 @@ import {
|
|
|
21
22
|
import type {LoadContext} from '@docusaurus/types';
|
|
22
23
|
|
|
23
24
|
import {getFileLastUpdate} from './lastUpdate';
|
|
24
|
-
import type {
|
|
25
|
-
DocFile,
|
|
26
|
-
DocMetadataBase,
|
|
27
|
-
DocMetadata,
|
|
28
|
-
DocNavLink,
|
|
29
|
-
LastUpdateData,
|
|
30
|
-
VersionMetadata,
|
|
31
|
-
LoadedVersion,
|
|
32
|
-
} from './types';
|
|
25
|
+
import type {DocFile} from './types';
|
|
33
26
|
import getSlug from './slug';
|
|
34
27
|
import {CURRENT_VERSION_NAME} from './constants';
|
|
35
|
-
import {getDocsDirPaths} from './versions';
|
|
36
28
|
import {stripPathNumberPrefixes} from './numberPrefix';
|
|
37
29
|
import {validateDocFrontMatter} from './frontMatter';
|
|
38
30
|
import type {SidebarsUtils} from './sidebars/utils';
|
|
@@ -41,7 +33,13 @@ import type {
|
|
|
41
33
|
MetadataOptions,
|
|
42
34
|
PluginOptions,
|
|
43
35
|
CategoryIndexMatcher,
|
|
44
|
-
|
|
36
|
+
DocMetadataBase,
|
|
37
|
+
DocMetadata,
|
|
38
|
+
PropNavigationLink,
|
|
39
|
+
LastUpdateData,
|
|
40
|
+
VersionMetadata,
|
|
41
|
+
DocFrontMatter,
|
|
42
|
+
LoadedVersion,
|
|
45
43
|
} from '@docusaurus/plugin-content-docs';
|
|
46
44
|
|
|
47
45
|
type LastUpdateOptions = Pick<
|
|
@@ -85,7 +83,7 @@ export async function readDocFile(
|
|
|
85
83
|
options: LastUpdateOptions,
|
|
86
84
|
): Promise<DocFile> {
|
|
87
85
|
const contentPath = await getFolderContainingFile(
|
|
88
|
-
|
|
86
|
+
getContentPathList(versionMetadata),
|
|
89
87
|
source,
|
|
90
88
|
);
|
|
91
89
|
|
|
@@ -114,16 +112,31 @@ export async function readVersionDocs(
|
|
|
114
112
|
);
|
|
115
113
|
}
|
|
116
114
|
|
|
115
|
+
export type DocEnv = 'production' | 'development';
|
|
116
|
+
|
|
117
|
+
/** Docs with draft front matter are only considered draft in production. */
|
|
118
|
+
function isDraftForEnvironment({
|
|
119
|
+
env,
|
|
120
|
+
frontMatter,
|
|
121
|
+
}: {
|
|
122
|
+
frontMatter: DocFrontMatter;
|
|
123
|
+
env: DocEnv;
|
|
124
|
+
}): boolean {
|
|
125
|
+
return (env === 'production' && frontMatter.draft) ?? false;
|
|
126
|
+
}
|
|
127
|
+
|
|
117
128
|
function doProcessDocMetadata({
|
|
118
129
|
docFile,
|
|
119
130
|
versionMetadata,
|
|
120
131
|
context,
|
|
121
132
|
options,
|
|
133
|
+
env,
|
|
122
134
|
}: {
|
|
123
135
|
docFile: DocFile;
|
|
124
136
|
versionMetadata: VersionMetadata;
|
|
125
137
|
context: LoadContext;
|
|
126
138
|
options: MetadataOptions;
|
|
139
|
+
env: DocEnv;
|
|
127
140
|
}): DocMetadataBase {
|
|
128
141
|
const {source, content, lastUpdate, contentPath, filePath} = docFile;
|
|
129
142
|
const {siteDir, i18n} = context;
|
|
@@ -144,15 +157,13 @@ function doProcessDocMetadata({
|
|
|
144
157
|
parse_number_prefixes: parseNumberPrefixes = true,
|
|
145
158
|
} = frontMatter;
|
|
146
159
|
|
|
147
|
-
//
|
|
148
|
-
// ex: myDoc -> myDoc
|
|
160
|
+
// E.g. api/plugins/myDoc -> myDoc; myDoc -> myDoc
|
|
149
161
|
const sourceFileNameWithoutExtension = path.basename(
|
|
150
162
|
source,
|
|
151
163
|
path.extname(source),
|
|
152
164
|
);
|
|
153
165
|
|
|
154
|
-
//
|
|
155
|
-
// ex: myDoc -> .
|
|
166
|
+
// E.g. api/plugins/myDoc -> api/plugins; myDoc -> .
|
|
156
167
|
const sourceDirName = path.dirname(source);
|
|
157
168
|
|
|
158
169
|
const {filename: unprefixedFileName, numberPrefix} = parseNumberPrefixes
|
|
@@ -213,7 +224,7 @@ function doProcessDocMetadata({
|
|
|
213
224
|
|
|
214
225
|
const description: string = frontMatter.description ?? excerpt ?? '';
|
|
215
226
|
|
|
216
|
-
const permalink = normalizeUrl([versionMetadata.
|
|
227
|
+
const permalink = normalizeUrl([versionMetadata.path, docSlug]);
|
|
217
228
|
|
|
218
229
|
function getDocEditUrl() {
|
|
219
230
|
const relativeFilePath = path.relative(contentPath, filePath);
|
|
@@ -232,13 +243,15 @@ function doProcessDocMetadata({
|
|
|
232
243
|
const isLocalized = contentPath === versionMetadata.contentPathLocalized;
|
|
233
244
|
const baseVersionEditUrl =
|
|
234
245
|
isLocalized && options.editLocalizedFiles
|
|
235
|
-
? versionMetadata.
|
|
236
|
-
: versionMetadata.
|
|
246
|
+
? versionMetadata.editUrlLocalized
|
|
247
|
+
: versionMetadata.editUrl;
|
|
237
248
|
return getEditUrl(relativeFilePath, baseVersionEditUrl);
|
|
238
249
|
}
|
|
239
250
|
return undefined;
|
|
240
251
|
}
|
|
241
252
|
|
|
253
|
+
const draft = isDraftForEnvironment({env, frontMatter});
|
|
254
|
+
|
|
242
255
|
// Assign all of object properties during instantiation (if possible) for
|
|
243
256
|
// NodeJS optimization.
|
|
244
257
|
// Adding properties to object after instantiation will cause hidden
|
|
@@ -252,15 +265,16 @@ function doProcessDocMetadata({
|
|
|
252
265
|
sourceDirName,
|
|
253
266
|
slug: docSlug,
|
|
254
267
|
permalink,
|
|
268
|
+
draft,
|
|
255
269
|
editUrl: customEditURL !== undefined ? customEditURL : getDocEditUrl(),
|
|
256
270
|
tags: normalizeFrontMatterTags(versionMetadata.tagsPath, frontMatter.tags),
|
|
257
271
|
version: versionMetadata.versionName,
|
|
258
272
|
lastUpdatedBy: lastUpdate.lastUpdatedBy,
|
|
259
273
|
lastUpdatedAt: lastUpdate.lastUpdatedAt,
|
|
260
274
|
formattedLastUpdatedAt: lastUpdate.lastUpdatedAt
|
|
261
|
-
? new Intl.DateTimeFormat(i18n.currentLocale
|
|
262
|
-
|
|
263
|
-
)
|
|
275
|
+
? new Intl.DateTimeFormat(i18n.currentLocale, {
|
|
276
|
+
calendar: i18n.localeConfigs[i18n.currentLocale]!.calendar,
|
|
277
|
+
}).format(lastUpdate.lastUpdatedAt * 1000)
|
|
264
278
|
: undefined,
|
|
265
279
|
sidebarPosition,
|
|
266
280
|
frontMatter,
|
|
@@ -272,6 +286,7 @@ export function processDocMetadata(args: {
|
|
|
272
286
|
versionMetadata: VersionMetadata;
|
|
273
287
|
context: LoadContext;
|
|
274
288
|
options: MetadataOptions;
|
|
289
|
+
env: DocEnv;
|
|
275
290
|
}): DocMetadataBase {
|
|
276
291
|
try {
|
|
277
292
|
return doProcessDocMetadata(args);
|
|
@@ -304,7 +319,7 @@ export function addDocNavigation(
|
|
|
304
319
|
const toNavigationLinkByDocId = (
|
|
305
320
|
docId: string | null | undefined,
|
|
306
321
|
type: 'prev' | 'next',
|
|
307
|
-
):
|
|
322
|
+
): PropNavigationLink | undefined => {
|
|
308
323
|
if (!docId) {
|
|
309
324
|
return undefined;
|
|
310
325
|
}
|
|
@@ -331,7 +346,7 @@ export function addDocNavigation(
|
|
|
331
346
|
}
|
|
332
347
|
|
|
333
348
|
const docsWithNavigation = docsBase.map(addNavData);
|
|
334
|
-
//
|
|
349
|
+
// Sort to ensure consistent output for tests
|
|
335
350
|
docsWithNavigation.sort((a, b) => a.id.localeCompare(b.id));
|
|
336
351
|
return docsWithNavigation;
|
|
337
352
|
}
|
|
@@ -401,7 +416,7 @@ export function toCategoryIndexMatcherParam({
|
|
|
401
416
|
}: Pick<
|
|
402
417
|
DocMetadataBase,
|
|
403
418
|
'source' | 'sourceDirName'
|
|
404
|
-
>):
|
|
419
|
+
>): Parameters<CategoryIndexMatcher>[0] {
|
|
405
420
|
// source + sourceDirName are always posix-style
|
|
406
421
|
return {
|
|
407
422
|
fileName: path.posix.parse(source).name,
|
|
@@ -418,7 +433,7 @@ export function getDocIds(doc: DocMetadataBase): [string, string] {
|
|
|
418
433
|
return [doc.unversionedId, doc.id];
|
|
419
434
|
}
|
|
420
435
|
|
|
421
|
-
//
|
|
436
|
+
// Docs are indexed by both versioned and unversioned ids at the same time
|
|
422
437
|
// TODO legacy retro-compatibility due to old versioned sidebars using
|
|
423
438
|
// versioned doc ids ("id" should be removed & "versionedId" should be renamed
|
|
424
439
|
// to "id")
|
package/src/frontMatter.ts
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
FrontMatterTOCHeadingLevels,
|
|
13
13
|
validateFrontMatter,
|
|
14
14
|
} from '@docusaurus/utils-validation';
|
|
15
|
-
import type {DocFrontMatter} from '
|
|
15
|
+
import type {DocFrontMatter} from '@docusaurus/plugin-content-docs';
|
|
16
16
|
|
|
17
17
|
// NOTE: we don't add any default value on purpose here
|
|
18
18
|
// We don't want default values to magically appear in doc metadata and props
|
|
@@ -20,12 +20,14 @@ import type {DocFrontMatter} from './types';
|
|
|
20
20
|
// We use default values in code instead
|
|
21
21
|
const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
|
|
22
22
|
id: Joi.string(),
|
|
23
|
-
|
|
23
|
+
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
|
24
|
+
title: Joi.string().allow(''),
|
|
24
25
|
hide_title: Joi.boolean(),
|
|
25
26
|
hide_table_of_contents: Joi.boolean(),
|
|
26
27
|
keywords: Joi.array().items(Joi.string().required()),
|
|
27
28
|
image: URISchema,
|
|
28
|
-
|
|
29
|
+
// See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398
|
|
30
|
+
description: Joi.string().allow(''),
|
|
29
31
|
slug: Joi.string(),
|
|
30
32
|
sidebar_label: Joi.string(),
|
|
31
33
|
sidebar_position: Joi.number(),
|
|
@@ -38,6 +40,7 @@ const DocFrontMatterSchema = Joi.object<DocFrontMatter>({
|
|
|
38
40
|
parse_number_prefixes: Joi.boolean(),
|
|
39
41
|
pagination_next: Joi.string().allow(null),
|
|
40
42
|
pagination_prev: Joi.string().allow(null),
|
|
43
|
+
draft: Joi.boolean(),
|
|
41
44
|
...FrontMatterTOCHeadingLevels,
|
|
42
45
|
}).unknown();
|
|
43
46
|
|
package/src/globalData.ts
CHANGED
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
|
|
8
8
|
import _ from 'lodash';
|
|
9
9
|
import type {Sidebars} from './sidebars/types';
|
|
10
|
-
import {
|
|
10
|
+
import {getMainDocId} from './docs';
|
|
11
|
+
import type {FullVersion} from './types';
|
|
11
12
|
import type {
|
|
12
13
|
CategoryGeneratedIndexMetadata,
|
|
13
14
|
DocMetadata,
|
|
14
|
-
|
|
15
|
-
} from './types';
|
|
15
|
+
} from '@docusaurus/plugin-content-docs';
|
|
16
16
|
import type {
|
|
17
17
|
GlobalVersion,
|
|
18
18
|
GlobalSidebar,
|
|
@@ -39,11 +39,10 @@ function toGlobalDataGeneratedIndex(
|
|
|
39
39
|
|
|
40
40
|
function toGlobalSidebars(
|
|
41
41
|
sidebars: Sidebars,
|
|
42
|
-
version:
|
|
42
|
+
version: FullVersion,
|
|
43
43
|
): {[sidebarId: string]: GlobalSidebar} {
|
|
44
|
-
const {getFirstLink} = createSidebarsUtils(sidebars);
|
|
45
44
|
return _.mapValues(sidebars, (sidebar, sidebarId) => {
|
|
46
|
-
const firstLink = getFirstLink(sidebarId);
|
|
45
|
+
const firstLink = version.sidebarsUtils.getFirstLink(sidebarId);
|
|
47
46
|
if (!firstLink) {
|
|
48
47
|
return {};
|
|
49
48
|
}
|
|
@@ -62,16 +61,17 @@ function toGlobalSidebars(
|
|
|
62
61
|
});
|
|
63
62
|
}
|
|
64
63
|
|
|
65
|
-
export function toGlobalDataVersion(version:
|
|
64
|
+
export function toGlobalDataVersion(version: FullVersion): GlobalVersion {
|
|
66
65
|
return {
|
|
67
66
|
name: version.versionName,
|
|
68
|
-
label: version.
|
|
67
|
+
label: version.label,
|
|
69
68
|
isLast: version.isLast,
|
|
70
|
-
path: version.
|
|
71
|
-
mainDocId: version
|
|
69
|
+
path: version.path,
|
|
70
|
+
mainDocId: getMainDocId(version),
|
|
72
71
|
docs: version.docs
|
|
73
72
|
.map(toGlobalDataDoc)
|
|
74
73
|
.concat(version.categoryGeneratedIndices.map(toGlobalDataGeneratedIndex)),
|
|
74
|
+
draftIds: version.drafts.map((doc) => doc.unversionedId),
|
|
75
75
|
sidebars: toGlobalSidebars(version.sidebars, version),
|
|
76
76
|
};
|
|
77
77
|
}
|