@docusaurus/core 3.2.1 → 3.3.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.
- package/bin/docusaurus.mjs +4 -0
- package/lib/client/App.d.ts +1 -0
- package/lib/client/App.js +9 -5
- package/lib/client/exports/ComponentCreator.js +3 -1
- package/lib/commands/deploy.d.ts +1 -0
- package/lib/commands/deploy.js +13 -13
- package/lib/commands/serve.js +19 -4
- package/lib/server/codegen/codegen.d.ts +2 -2
- package/lib/server/codegen/codegenRoutes.d.ts +3 -3
- package/lib/server/codegen/codegenRoutes.js +77 -8
- package/lib/server/configValidation.js +9 -3
- package/lib/server/plugins/actions.js +11 -3
- package/lib/server/plugins/plugins.d.ts +2 -2
- package/lib/server/plugins/plugins.js +12 -13
- package/lib/server/plugins/pluginsUtils.d.ts +6 -2
- package/lib/server/plugins/pluginsUtils.js +6 -2
- package/lib/server/plugins/routeConfig.d.ts +2 -2
- package/lib/server/plugins/routeConfig.js +20 -2
- package/lib/ssg.d.ts +1 -0
- package/lib/webpack/base.js +2 -2
- package/lib/webpack/utils.d.ts +2 -2
- package/package.json +13 -15
package/bin/docusaurus.mjs
CHANGED
package/lib/client/App.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
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
|
+
/// <reference types="@docusaurus/module-type-aliases" />
|
|
7
8
|
/// <reference types="react" />
|
|
8
9
|
import '@generated/client-modules';
|
|
9
10
|
export default function App(): JSX.Element;
|
package/lib/client/App.js
CHANGED
|
@@ -21,9 +21,15 @@ import SiteMetadataDefaults from './SiteMetadataDefaults';
|
|
|
21
21
|
// eslint-disable-next-line import/order
|
|
22
22
|
import ErrorBoundary from '@docusaurus/ErrorBoundary';
|
|
23
23
|
import HasHydratedDataAttribute from './hasHydratedDataAttribute';
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const routesElement = renderRoutes(routes);
|
|
25
|
+
function AppNavigation() {
|
|
26
26
|
const location = useLocation();
|
|
27
|
+
const normalizedLocation = normalizeLocation(location);
|
|
28
|
+
return (<PendingNavigation location={normalizedLocation}>
|
|
29
|
+
{routesElement}
|
|
30
|
+
</PendingNavigation>);
|
|
31
|
+
}
|
|
32
|
+
export default function App() {
|
|
27
33
|
return (<ErrorBoundary>
|
|
28
34
|
<DocusaurusContextProvider>
|
|
29
35
|
<BrowserContextProvider>
|
|
@@ -31,9 +37,7 @@ export default function App() {
|
|
|
31
37
|
<SiteMetadataDefaults />
|
|
32
38
|
<SiteMetadata />
|
|
33
39
|
<BaseUrlIssueBanner />
|
|
34
|
-
<
|
|
35
|
-
{routeElement}
|
|
36
|
-
</PendingNavigation>
|
|
40
|
+
<AppNavigation />
|
|
37
41
|
</Root>
|
|
38
42
|
<HasHydratedDataAttribute />
|
|
39
43
|
</BrowserContextProvider>
|
|
@@ -100,10 +100,12 @@ export default function ComponentCreator(path, hash) {
|
|
|
100
100
|
delete loadedModules.__comp;
|
|
101
101
|
const routeContext = loadedModules.__context;
|
|
102
102
|
delete loadedModules.__context;
|
|
103
|
+
const routeProps = loadedModules.__props;
|
|
104
|
+
delete loadedModules.__props;
|
|
103
105
|
/* eslint-enable no-underscore-dangle */
|
|
104
106
|
// Is there any way to put this RouteContextProvider upper in the tree?
|
|
105
107
|
return (<RouteContextProvider value={routeContext}>
|
|
106
|
-
<Component {...loadedModules} {...props}/>
|
|
108
|
+
<Component {...loadedModules} {...routeProps} {...props}/>
|
|
107
109
|
</RouteContextProvider>);
|
|
108
110
|
},
|
|
109
111
|
});
|
package/lib/commands/deploy.d.ts
CHANGED
|
@@ -7,5 +7,6 @@
|
|
|
7
7
|
import { type LoadContextParams } from '../server/site';
|
|
8
8
|
export type DeployCLIOptions = Pick<LoadContextParams, 'config' | 'locale' | 'outDir'> & {
|
|
9
9
|
skipBuild?: boolean;
|
|
10
|
+
targetDir?: string;
|
|
10
11
|
};
|
|
11
12
|
export declare function deploy(siteDirParam?: string, cliOptions?: Partial<DeployCLIOptions>): Promise<void>;
|
package/lib/commands/deploy.js
CHANGED
|
@@ -128,26 +128,25 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
|
|
|
128
128
|
// out to deployment branch.
|
|
129
129
|
const currentCommit = shellExecLog('git rev-parse HEAD').stdout.trim();
|
|
130
130
|
const runDeploy = async (outputDirectory) => {
|
|
131
|
+
const targetDirectory = cliOptions.targetDir ?? '.';
|
|
131
132
|
const fromPath = outputDirectory;
|
|
132
133
|
const toPath = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), `${projectName}-${deploymentBranch}`));
|
|
133
134
|
shelljs_1.default.cd(toPath);
|
|
134
|
-
//
|
|
135
|
-
// the
|
|
136
|
-
//
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
shellExecLog('git rm -rf .');
|
|
140
|
-
}
|
|
141
|
-
else {
|
|
142
|
-
shellExecLog('git init');
|
|
135
|
+
// Clones the repo into the temp folder and checks out the target branch.
|
|
136
|
+
// If the branch doesn't exist, it creates a new one based on the
|
|
137
|
+
// repository default branch.
|
|
138
|
+
if (shellExecLog(`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} "${toPath}"`).code !== 0) {
|
|
139
|
+
shellExecLog(`git clone --depth 1 ${deploymentRepoURL} "${toPath}"`);
|
|
143
140
|
shellExecLog(`git checkout -b ${deploymentBranch}`);
|
|
144
|
-
shellExecLog(`git remote add origin ${deploymentRepoURL}`);
|
|
145
141
|
}
|
|
142
|
+
// Clear out any existing contents in the target directory
|
|
143
|
+
shellExecLog(`git rm -rf ${targetDirectory}`);
|
|
144
|
+
const targetPath = path_1.default.join(toPath, targetDirectory);
|
|
146
145
|
try {
|
|
147
|
-
await fs_extra_1.default.copy(fromPath,
|
|
146
|
+
await fs_extra_1.default.copy(fromPath, targetPath);
|
|
148
147
|
}
|
|
149
148
|
catch (err) {
|
|
150
|
-
logger_1.default.error `Copying build assets from path=${fromPath} to path=${
|
|
149
|
+
logger_1.default.error `Copying build assets from path=${fromPath} to path=${targetPath} failed.`;
|
|
151
150
|
throw err;
|
|
152
151
|
}
|
|
153
152
|
shellExecLog('git add --all');
|
|
@@ -184,7 +183,8 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
|
|
|
184
183
|
if (!cliOptions.skipBuild) {
|
|
185
184
|
// Build site, then push to deploymentBranch branch of specified repo.
|
|
186
185
|
try {
|
|
187
|
-
await (0, build_1.build)(siteDir, cliOptions, false)
|
|
186
|
+
await (0, build_1.build)(siteDir, cliOptions, false);
|
|
187
|
+
await runDeploy(outDir);
|
|
188
188
|
}
|
|
189
189
|
catch (err) {
|
|
190
190
|
logger_1.default.error('Deployment of the build output failed.');
|
package/lib/commands/serve.js
CHANGED
|
@@ -15,9 +15,16 @@ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
|
15
15
|
const utils_1 = require("@docusaurus/utils");
|
|
16
16
|
const serve_handler_1 = tslib_1.__importDefault(require("serve-handler"));
|
|
17
17
|
const openBrowser_1 = tslib_1.__importDefault(require("react-dev-utils/openBrowser"));
|
|
18
|
+
const utils_common_1 = require("@docusaurus/utils-common");
|
|
18
19
|
const config_1 = require("../server/config");
|
|
19
20
|
const build_1 = require("./build");
|
|
20
21
|
const getHostPort_1 = require("../server/getHostPort");
|
|
22
|
+
function redirect(res, location) {
|
|
23
|
+
res.writeHead(302, {
|
|
24
|
+
Location: location,
|
|
25
|
+
});
|
|
26
|
+
res.end();
|
|
27
|
+
}
|
|
21
28
|
async function serve(siteDirParam = '.', cliOptions = {}) {
|
|
22
29
|
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
|
|
23
30
|
const buildDir = cliOptions.dir ?? utils_1.DEFAULT_BUILD_DIR_NAME;
|
|
@@ -40,14 +47,22 @@ async function serve(siteDirParam = '.', cliOptions = {}) {
|
|
|
40
47
|
const server = http_1.default.createServer((req, res) => {
|
|
41
48
|
// Automatically redirect requests to /baseUrl/
|
|
42
49
|
if (!req.url?.startsWith(baseUrl)) {
|
|
43
|
-
res
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
redirect(res, baseUrl);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
// We do the redirect ourselves for a good reason
|
|
54
|
+
// server-handler is annoying and won't include /baseUrl/ in redirects
|
|
55
|
+
const normalizedUrl = (0, utils_common_1.applyTrailingSlash)(req.url, { trailingSlash, baseUrl });
|
|
56
|
+
if (req.url !== normalizedUrl) {
|
|
57
|
+
redirect(res, normalizedUrl);
|
|
47
58
|
return;
|
|
48
59
|
}
|
|
49
60
|
// Remove baseUrl before calling serveHandler, because /baseUrl/ should
|
|
50
61
|
// serve /build/index.html, not /build/baseUrl/index.html (does not exist)
|
|
62
|
+
// Note server-handler is really annoying here:
|
|
63
|
+
// - no easy way to do rewrites such as "/baseUrl/:path" => "/:path"
|
|
64
|
+
// - no easy way to "reapply" the baseUrl to the redirect "Location" header
|
|
65
|
+
// See also https://github.com/facebook/docusaurus/pull/10090
|
|
51
66
|
req.url = req.url.replace(baseUrl, '/');
|
|
52
67
|
(0, serve_handler_1.default)(req, res, {
|
|
53
68
|
cleanUrls: true,
|
|
@@ -4,7 +4,7 @@
|
|
|
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 { CodeTranslations, DocusaurusConfig, GlobalData, I18n,
|
|
7
|
+
import type { CodeTranslations, DocusaurusConfig, GlobalData, I18n, PluginRouteConfig, SiteMetadata } from '@docusaurus/types';
|
|
8
8
|
type CodegenParams = {
|
|
9
9
|
generatedFilesDir: string;
|
|
10
10
|
siteConfig: DocusaurusConfig;
|
|
@@ -14,7 +14,7 @@ type CodegenParams = {
|
|
|
14
14
|
i18n: I18n;
|
|
15
15
|
codeTranslations: CodeTranslations;
|
|
16
16
|
siteMetadata: SiteMetadata;
|
|
17
|
-
routes:
|
|
17
|
+
routes: PluginRouteConfig[];
|
|
18
18
|
};
|
|
19
19
|
export declare function generateSiteFiles(params: CodegenParams): Promise<void>;
|
|
20
20
|
export {};
|
|
@@ -4,7 +4,7 @@
|
|
|
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 { RouteConfig, RouteChunkNames } from '@docusaurus/types';
|
|
7
|
+
import type { RouteConfig, RouteChunkNames, PluginRouteConfig } from '@docusaurus/types';
|
|
8
8
|
type RoutesCode = {
|
|
9
9
|
/** Serialized routes config that can be directly emitted into temp file. */
|
|
10
10
|
routesConfig: string;
|
|
@@ -42,8 +42,8 @@ export declare function genChunkName(modulePath: string, prefix?: string, prefer
|
|
|
42
42
|
export declare function generateRoutesCode(routeConfigs: RouteConfig[]): RoutesCode;
|
|
43
43
|
type GenerateRouteFilesParams = {
|
|
44
44
|
generatedFilesDir: string;
|
|
45
|
-
routes:
|
|
45
|
+
routes: PluginRouteConfig[];
|
|
46
46
|
baseUrl: string;
|
|
47
47
|
};
|
|
48
|
-
export declare function generateRouteFiles({ generatedFilesDir, routes, }: GenerateRouteFilesParams): Promise<void>;
|
|
48
|
+
export declare function generateRouteFiles({ generatedFilesDir, routes: initialRoutes, }: GenerateRouteFilesParams): Promise<void>;
|
|
49
49
|
export {};
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.generateRouteFiles = exports.generateRoutesCode = exports.genChunkName = void 0;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const querystring_1 = tslib_1.__importDefault(require("querystring"));
|
|
12
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
12
13
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
13
14
|
const utils_1 = require("@docusaurus/utils");
|
|
14
15
|
/** Indents every line of `str` by one level. */
|
|
@@ -58,7 +59,7 @@ exports.genChunkName = genChunkName;
|
|
|
58
59
|
* is the same as react-router's `RouteConfig`. Formatting is similar to
|
|
59
60
|
* `JSON.stringify` but without all the quotes.
|
|
60
61
|
*/
|
|
61
|
-
function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeStrings,
|
|
62
|
+
function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeStrings, attributes, }) {
|
|
62
63
|
const parts = [
|
|
63
64
|
`path: '${routePath}'`,
|
|
64
65
|
`component: ComponentCreator('${routePath}', '${routeHash}')`,
|
|
@@ -71,10 +72,10 @@ function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeString
|
|
|
71
72
|
${indent(subroutesCodeStrings.join(',\n'))}
|
|
72
73
|
]`);
|
|
73
74
|
}
|
|
74
|
-
Object.entries(
|
|
75
|
-
const isIdentifier = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(
|
|
76
|
-
const key = isIdentifier ?
|
|
77
|
-
parts.push(`${key}: ${JSON.stringify(
|
|
75
|
+
Object.entries(attributes).forEach(([attrName, attrValue]) => {
|
|
76
|
+
const isIdentifier = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(attrName);
|
|
77
|
+
const key = isIdentifier ? attrName : JSON.stringify(attrName);
|
|
78
|
+
parts.push(`${key}: ${JSON.stringify(attrValue)}`);
|
|
78
79
|
});
|
|
79
80
|
return `{
|
|
80
81
|
${indent(parts.join(',\n'))}
|
|
@@ -114,7 +115,7 @@ function genChunkNames(routeModule, prefix, name, res) {
|
|
|
114
115
|
* `routesPaths`, and `routesChunkNames` accordingly.
|
|
115
116
|
*/
|
|
116
117
|
function genRouteCode(routeConfig, res) {
|
|
117
|
-
const { path: routePath, component, modules = {}, context, routes: subroutes, priority, exact, metadata,
|
|
118
|
+
const { path: routePath, component, modules = {}, context, routes: subroutes, priority, exact, metadata, props, plugin, ...attributes } = routeConfig;
|
|
118
119
|
if (typeof routePath !== 'string' || !component) {
|
|
119
120
|
throw new Error(`Invalid route config: path must be a string and component is required.
|
|
120
121
|
${JSON.stringify(routeConfig)}`);
|
|
@@ -132,7 +133,7 @@ ${JSON.stringify(routeConfig)}`);
|
|
|
132
133
|
routeHash,
|
|
133
134
|
subroutesCodeStrings: subroutes?.map((r) => genRouteCode(r, res)),
|
|
134
135
|
exact,
|
|
135
|
-
|
|
136
|
+
attributes,
|
|
136
137
|
});
|
|
137
138
|
}
|
|
138
139
|
/**
|
|
@@ -179,7 +180,75 @@ ${Object.entries(registry)
|
|
|
179
180
|
`);
|
|
180
181
|
const genRoutesChunkNames = ({ generatedFilesDir, routesChunkNames, }) => (0, utils_1.generate)(generatedFilesDir, 'routesChunkNames.json', JSON.stringify(routesChunkNames, null, 2));
|
|
181
182
|
const genRoutes = ({ generatedFilesDir, routesConfig, }) => (0, utils_1.generate)(generatedFilesDir, 'routes.js', routesConfig);
|
|
182
|
-
async function
|
|
183
|
+
async function generateRoutePropModule({ generatedFilesDir, route, plugin, }) {
|
|
184
|
+
ensureNoPropsConflict(route);
|
|
185
|
+
const moduleContent = JSON.stringify(route.props);
|
|
186
|
+
// TODO we should aim to reduce this path length
|
|
187
|
+
// This adds bytes to the global module registry
|
|
188
|
+
const relativePath = path_1.default.posix.join(plugin.name, plugin.id, 'p', `${(0, utils_1.docuHash)(route.path)}.json`);
|
|
189
|
+
const modulePath = path_1.default.posix.join(generatedFilesDir, relativePath);
|
|
190
|
+
const aliasedPath = path_1.default.posix.join('@generated', relativePath);
|
|
191
|
+
await (0, utils_1.generate)(generatedFilesDir, modulePath, moduleContent);
|
|
192
|
+
return aliasedPath;
|
|
193
|
+
}
|
|
194
|
+
function ensureNoPropsConflict(route) {
|
|
195
|
+
if (!route.props && !route.modules) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const conflictingPropNames = lodash_1.default.intersection(Object.keys(route.props ?? {}), Object.keys(route.modules ?? {}));
|
|
199
|
+
if (conflictingPropNames.length > 0) {
|
|
200
|
+
throw new Error(`Route ${route.path} has conflicting props declared using both route.modules and route.props APIs for keys: ${conflictingPropNames.join(', ')}\nThis is not permitted, otherwise one prop would override the over.`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
async function preprocessRouteProps({ generatedFilesDir, route, plugin, }) {
|
|
204
|
+
const propsModulePathPromise = route.props
|
|
205
|
+
? generateRoutePropModule({
|
|
206
|
+
generatedFilesDir,
|
|
207
|
+
route,
|
|
208
|
+
plugin,
|
|
209
|
+
})
|
|
210
|
+
: undefined;
|
|
211
|
+
const subRoutesPromise = route.routes
|
|
212
|
+
? Promise.all(route.routes.map((subRoute) => {
|
|
213
|
+
return preprocessRouteProps({
|
|
214
|
+
generatedFilesDir,
|
|
215
|
+
route: subRoute,
|
|
216
|
+
plugin,
|
|
217
|
+
});
|
|
218
|
+
}))
|
|
219
|
+
: undefined;
|
|
220
|
+
const [propsModulePath, subRoutes] = await Promise.all([
|
|
221
|
+
propsModulePathPromise,
|
|
222
|
+
subRoutesPromise,
|
|
223
|
+
]);
|
|
224
|
+
const newRoute = {
|
|
225
|
+
...route,
|
|
226
|
+
modules: {
|
|
227
|
+
...route.modules,
|
|
228
|
+
...(propsModulePath && { __props: propsModulePath }),
|
|
229
|
+
},
|
|
230
|
+
routes: subRoutes,
|
|
231
|
+
props: undefined,
|
|
232
|
+
};
|
|
233
|
+
return newRoute;
|
|
234
|
+
}
|
|
235
|
+
// For convenience, it's possible to pass a "route.props" object
|
|
236
|
+
// This method converts the props object to a regular module
|
|
237
|
+
// and assigns it to route.modules.__props attribute
|
|
238
|
+
async function preprocessAllPluginsRoutesProps({ generatedFilesDir, routes, }) {
|
|
239
|
+
return Promise.all(routes.map((route) => {
|
|
240
|
+
return preprocessRouteProps({
|
|
241
|
+
generatedFilesDir,
|
|
242
|
+
route,
|
|
243
|
+
plugin: route.plugin,
|
|
244
|
+
});
|
|
245
|
+
}));
|
|
246
|
+
}
|
|
247
|
+
async function generateRouteFiles({ generatedFilesDir, routes: initialRoutes, }) {
|
|
248
|
+
const routes = await preprocessAllPluginsRoutesProps({
|
|
249
|
+
generatedFilesDir,
|
|
250
|
+
routes: initialRoutes,
|
|
251
|
+
});
|
|
183
252
|
const { registry, routesChunkNames, routesConfig } = generateRoutesCode(routes);
|
|
184
253
|
await Promise.all([
|
|
185
254
|
genRegistry({ generatedFilesDir, registry }),
|
|
@@ -18,7 +18,7 @@ exports.DEFAULT_I18N_CONFIG = {
|
|
|
18
18
|
localeConfigs: {},
|
|
19
19
|
};
|
|
20
20
|
exports.DEFAULT_MARKDOWN_CONFIG = {
|
|
21
|
-
format: 'mdx',
|
|
21
|
+
format: 'mdx', // TODO change this to "detect" in Docusaurus v4?
|
|
22
22
|
mermaid: false,
|
|
23
23
|
preprocessor: undefined,
|
|
24
24
|
parseFrontMatter: utils_1.DEFAULT_PARSE_FRONT_MATTER,
|
|
@@ -27,12 +27,15 @@ exports.DEFAULT_MARKDOWN_CONFIG = {
|
|
|
27
27
|
admonitions: true,
|
|
28
28
|
headingIds: true,
|
|
29
29
|
},
|
|
30
|
+
anchors: {
|
|
31
|
+
maintainCase: false,
|
|
32
|
+
},
|
|
30
33
|
remarkRehypeOptions: undefined,
|
|
31
34
|
};
|
|
32
35
|
exports.DEFAULT_CONFIG = {
|
|
33
36
|
i18n: exports.DEFAULT_I18N_CONFIG,
|
|
34
37
|
onBrokenLinks: 'throw',
|
|
35
|
-
onBrokenAnchors: 'warn',
|
|
38
|
+
onBrokenAnchors: 'warn', // TODO Docusaurus v4: change to throw
|
|
36
39
|
onBrokenMarkdownLinks: 'warn',
|
|
37
40
|
onDuplicateRoutes: 'warn',
|
|
38
41
|
plugins: [],
|
|
@@ -147,7 +150,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
|
|
|
147
150
|
favicon: utils_validation_1.Joi.string().optional(),
|
|
148
151
|
title: utils_validation_1.Joi.string().required(),
|
|
149
152
|
url: SiteUrlSchema,
|
|
150
|
-
trailingSlash: utils_validation_1.Joi.boolean(),
|
|
153
|
+
trailingSlash: utils_validation_1.Joi.boolean(), // No default value! undefined = retrocompatible legacy behavior!
|
|
151
154
|
i18n: I18N_CONFIG_SCHEMA,
|
|
152
155
|
onBrokenLinks: utils_validation_1.Joi.string()
|
|
153
156
|
.equal('ignore', 'log', 'warn', 'throw')
|
|
@@ -238,6 +241,9 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
|
|
|
238
241
|
// Not sure if it's a good idea, validation is likely to become stale
|
|
239
242
|
// See https://github.com/remarkjs/remark-rehype#options
|
|
240
243
|
utils_validation_1.Joi.object().unknown(),
|
|
244
|
+
anchors: utils_validation_1.Joi.object({
|
|
245
|
+
maintainCase: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.markdown.anchors.maintainCase),
|
|
246
|
+
}).default(exports.DEFAULT_CONFIG.markdown.anchors),
|
|
241
247
|
}).default(exports.DEFAULT_CONFIG.markdown),
|
|
242
248
|
}).messages({
|
|
243
249
|
'docusaurus.configValidationWarning': 'Docusaurus config validation warning. Field {#label}: {#warningMessage}',
|
|
@@ -18,13 +18,20 @@ const routeConfig_1 = require("./routeConfig");
|
|
|
18
18
|
async function createPluginActionsUtils({ plugin, generatedFilesDir, baseUrl, trailingSlash, }) {
|
|
19
19
|
const pluginId = plugin.options.id;
|
|
20
20
|
// Plugins data files are namespaced by pluginName/pluginId
|
|
21
|
+
// TODO Docusaurus v4 breaking change
|
|
22
|
+
// module aliasing should be automatic
|
|
23
|
+
// we should never find local absolute FS paths in the codegen registry
|
|
24
|
+
const aliasedSource = (source) => `@generated/${(0, utils_1.posixPath)(path_1.default.relative(generatedFilesDir, source))}`;
|
|
25
|
+
// TODO use @generated data dir here!
|
|
26
|
+
// The module registry should not contain absolute paths
|
|
21
27
|
const dataDir = path_1.default.join(generatedFilesDir, plugin.name, pluginId);
|
|
22
28
|
const pluginRouteContext = {
|
|
23
29
|
name: plugin.name,
|
|
24
30
|
id: pluginId,
|
|
25
31
|
};
|
|
26
|
-
const pluginRouteContextModulePath = path_1.default.join(dataDir,
|
|
32
|
+
const pluginRouteContextModulePath = path_1.default.join(dataDir, `__plugin.json`);
|
|
27
33
|
// TODO not ideal place to generate that file
|
|
34
|
+
// move to codegen step instead!
|
|
28
35
|
await (0, utils_1.generate)('/', pluginRouteContextModulePath, JSON.stringify(pluginRouteContext, null, 2));
|
|
29
36
|
const routes = [];
|
|
30
37
|
let globalData;
|
|
@@ -39,13 +46,14 @@ async function createPluginActionsUtils({ plugin, generatedFilesDir, baseUrl, tr
|
|
|
39
46
|
...finalRouteConfig,
|
|
40
47
|
context: {
|
|
41
48
|
...(finalRouteConfig.context && { data: finalRouteConfig.context }),
|
|
42
|
-
plugin: pluginRouteContextModulePath,
|
|
49
|
+
plugin: aliasedSource(pluginRouteContextModulePath),
|
|
43
50
|
},
|
|
44
51
|
});
|
|
45
52
|
},
|
|
46
53
|
async createData(name, data) {
|
|
47
54
|
const modulePath = path_1.default.join(dataDir, name);
|
|
48
|
-
|
|
55
|
+
const dataString = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
56
|
+
await (0, utils_1.generate)(dataDir, name, dataString);
|
|
49
57
|
return modulePath;
|
|
50
58
|
},
|
|
51
59
|
setGlobalData(data) {
|
|
@@ -4,10 +4,10 @@
|
|
|
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 { LoadContext,
|
|
7
|
+
import type { LoadContext, GlobalData, PluginIdentifier, LoadedPlugin, PluginRouteConfig } from '@docusaurus/types';
|
|
8
8
|
export type LoadPluginsResult = {
|
|
9
9
|
plugins: LoadedPlugin[];
|
|
10
|
-
routes:
|
|
10
|
+
routes: PluginRouteConfig[];
|
|
11
11
|
globalData: GlobalData;
|
|
12
12
|
};
|
|
13
13
|
/**
|
|
@@ -101,33 +101,30 @@ async function executePluginAllContentLoaded({ plugin, context, allContent, }) {
|
|
|
101
101
|
async function executeAllPluginsAllContentLoaded({ plugins, context, }) {
|
|
102
102
|
return utils_1.PerfLogger.async(`allContentLoaded()`, async () => {
|
|
103
103
|
const allContent = (0, pluginsUtils_1.aggregateAllContent)(plugins);
|
|
104
|
-
const
|
|
105
|
-
const
|
|
104
|
+
const allRoutes = [];
|
|
105
|
+
const allGlobalData = {};
|
|
106
106
|
await Promise.all(plugins.map(async (plugin) => {
|
|
107
107
|
var _a;
|
|
108
|
-
const { routes
|
|
108
|
+
const { routes, globalData: pluginGlobalData } = await executePluginAllContentLoaded({
|
|
109
109
|
plugin,
|
|
110
110
|
context,
|
|
111
111
|
allContent,
|
|
112
112
|
});
|
|
113
|
-
routes.
|
|
113
|
+
const pluginRoutes = routes.map((route) => (0, pluginsUtils_1.toPluginRoute)({ plugin, route }));
|
|
114
|
+
allRoutes.push(...pluginRoutes);
|
|
114
115
|
if (pluginGlobalData !== undefined) {
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
allGlobalData[_a = plugin.name] ?? (allGlobalData[_a] = {});
|
|
117
|
+
allGlobalData[plugin.name][plugin.options.id] = pluginGlobalData;
|
|
117
118
|
}
|
|
118
119
|
}));
|
|
119
|
-
return { routes, globalData };
|
|
120
|
+
return { routes: allRoutes, globalData: allGlobalData };
|
|
120
121
|
});
|
|
121
122
|
}
|
|
122
123
|
// This merges plugins routes and global data created from both lifecycles:
|
|
123
124
|
// - contentLoaded()
|
|
124
125
|
// - allContentLoaded()
|
|
125
|
-
function mergeResults({ plugins, allContentLoadedResult, }) {
|
|
126
|
-
const routes = [
|
|
127
|
-
...(0, pluginsUtils_1.aggregateRoutes)(plugins),
|
|
128
|
-
...allContentLoadedResult.routes,
|
|
129
|
-
];
|
|
130
|
-
(0, routeConfig_1.sortRoutes)(routes);
|
|
126
|
+
function mergeResults({ baseUrl, plugins, allContentLoadedResult, }) {
|
|
127
|
+
const routes = (0, routeConfig_1.sortRoutes)([...(0, pluginsUtils_1.aggregateRoutes)(plugins), ...allContentLoadedResult.routes], baseUrl);
|
|
131
128
|
const globalData = (0, pluginsUtils_1.mergeGlobalData)((0, pluginsUtils_1.aggregateGlobalData)(plugins), allContentLoadedResult.globalData);
|
|
132
129
|
return { routes, globalData };
|
|
133
130
|
}
|
|
@@ -148,6 +145,7 @@ async function loadPlugins(context) {
|
|
|
148
145
|
context,
|
|
149
146
|
});
|
|
150
147
|
const { routes, globalData } = mergeResults({
|
|
148
|
+
baseUrl: context.baseUrl,
|
|
151
149
|
plugins,
|
|
152
150
|
allContentLoadedResult,
|
|
153
151
|
});
|
|
@@ -179,6 +177,7 @@ async function reloadPlugin({ pluginIdentifier, plugins: previousPlugins, contex
|
|
|
179
177
|
context,
|
|
180
178
|
});
|
|
181
179
|
const { routes, globalData } = mergeResults({
|
|
180
|
+
baseUrl: context.baseUrl,
|
|
182
181
|
plugins,
|
|
183
182
|
allContentLoadedResult,
|
|
184
183
|
});
|
|
@@ -4,13 +4,17 @@
|
|
|
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 { AllContent, GlobalData, InitializedPlugin, LoadedPlugin, PluginIdentifier, RouteConfig } from '@docusaurus/types';
|
|
7
|
+
import type { AllContent, GlobalData, InitializedPlugin, LoadedPlugin, PluginIdentifier, PluginRouteConfig, RouteConfig } from '@docusaurus/types';
|
|
8
8
|
export declare function getPluginByIdentifier<P extends InitializedPlugin>({ plugins, pluginIdentifier, }: {
|
|
9
9
|
pluginIdentifier: PluginIdentifier;
|
|
10
10
|
plugins: P[];
|
|
11
11
|
}): P;
|
|
12
12
|
export declare function aggregateAllContent(loadedPlugins: LoadedPlugin[]): AllContent;
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function toPluginRoute({ plugin, route, }: {
|
|
14
|
+
plugin: LoadedPlugin;
|
|
15
|
+
route: RouteConfig;
|
|
16
|
+
}): PluginRouteConfig;
|
|
17
|
+
export declare function aggregateRoutes(loadedPlugins: LoadedPlugin[]): PluginRouteConfig[];
|
|
14
18
|
export declare function aggregateGlobalData(loadedPlugins: LoadedPlugin[]): GlobalData;
|
|
15
19
|
export declare function mergeGlobalData(...globalDataList: GlobalData[]): GlobalData;
|
|
16
20
|
export declare function formatPluginName(plugin: InitializedPlugin | PluginIdentifier): string;
|
|
@@ -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.formatPluginName = exports.mergeGlobalData = exports.aggregateGlobalData = exports.aggregateRoutes = exports.aggregateAllContent = exports.getPluginByIdentifier = void 0;
|
|
9
|
+
exports.formatPluginName = exports.mergeGlobalData = exports.aggregateGlobalData = exports.aggregateRoutes = exports.toPluginRoute = exports.aggregateAllContent = exports.getPluginByIdentifier = void 0;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
12
12
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
@@ -28,8 +28,12 @@ function aggregateAllContent(loadedPlugins) {
|
|
|
28
28
|
.value();
|
|
29
29
|
}
|
|
30
30
|
exports.aggregateAllContent = aggregateAllContent;
|
|
31
|
+
function toPluginRoute({ plugin, route, }) {
|
|
32
|
+
return { plugin: { name: plugin.name, id: plugin.options.id }, ...route };
|
|
33
|
+
}
|
|
34
|
+
exports.toPluginRoute = toPluginRoute;
|
|
31
35
|
function aggregateRoutes(loadedPlugins) {
|
|
32
|
-
return loadedPlugins.flatMap((
|
|
36
|
+
return loadedPlugins.flatMap((plugin) => plugin.routes.map((route) => toPluginRoute({ plugin, route })));
|
|
33
37
|
}
|
|
34
38
|
exports.aggregateRoutes = aggregateRoutes;
|
|
35
39
|
function aggregateGlobalData(loadedPlugins) {
|
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
import { type ApplyTrailingSlashParams } from '@docusaurus/utils-common';
|
|
8
8
|
import type { RouteConfig } from '@docusaurus/types';
|
|
9
9
|
/** Recursively applies trailing slash config to all nested routes. */
|
|
10
|
-
export declare function applyRouteTrailingSlash(route:
|
|
11
|
-
export declare function sortRoutes(
|
|
10
|
+
export declare function applyRouteTrailingSlash<Route extends RouteConfig>(route: Route, params: ApplyTrailingSlashParams): Route;
|
|
11
|
+
export declare function sortRoutes<Route extends RouteConfig>(routesToSort: Route[], baseUrl: string): Route[];
|
|
@@ -19,7 +19,8 @@ function applyRouteTrailingSlash(route, params) {
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
exports.applyRouteTrailingSlash = applyRouteTrailingSlash;
|
|
22
|
-
function sortRoutes(
|
|
22
|
+
function sortRoutes(routesToSort, baseUrl) {
|
|
23
|
+
const routeConfigs = [...routesToSort];
|
|
23
24
|
// Sort the route config. This ensures that route with nested
|
|
24
25
|
// routes is always placed last.
|
|
25
26
|
routeConfigs.sort((a, b) => {
|
|
@@ -36,6 +37,22 @@ function sortRoutes(routeConfigs, baseUrl = '/') {
|
|
|
36
37
|
if (!a.routes && b.routes) {
|
|
37
38
|
return -1;
|
|
38
39
|
}
|
|
40
|
+
// If both are parent routes (for example routeBasePath: "/" and "/docs/"
|
|
41
|
+
// We must order them carefully in case of overlapping paths
|
|
42
|
+
if (a.routes && b.routes) {
|
|
43
|
+
if (a.path === b.path) {
|
|
44
|
+
// We don't really support that kind of routing ATM
|
|
45
|
+
// React-Router by default will only "enter" a single parent route
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
if (a.path.includes(b.path)) {
|
|
49
|
+
return -1;
|
|
50
|
+
}
|
|
51
|
+
if (b.path.includes(a.path)) {
|
|
52
|
+
return 1;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
39
56
|
// Higher priority get placed first.
|
|
40
57
|
if (a.priority || b.priority) {
|
|
41
58
|
const priorityA = a.priority ?? 0;
|
|
@@ -49,8 +66,9 @@ function sortRoutes(routeConfigs, baseUrl = '/') {
|
|
|
49
66
|
});
|
|
50
67
|
routeConfigs.forEach((routeConfig) => {
|
|
51
68
|
if (routeConfig.routes) {
|
|
52
|
-
sortRoutes(routeConfig.routes, baseUrl);
|
|
69
|
+
routeConfig.routes = sortRoutes(routeConfig.routes, baseUrl);
|
|
53
70
|
}
|
|
54
71
|
});
|
|
72
|
+
return routeConfigs;
|
|
55
73
|
}
|
|
56
74
|
exports.sortRoutes = sortRoutes;
|
package/lib/ssg.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
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
|
+
/// <reference path="../src/deps.d.ts" />
|
|
7
8
|
import type { AppRenderer, SiteCollectedData } from './common';
|
|
8
9
|
import type { Manifest } from 'react-loadable-ssr-addon-v5-slorber';
|
|
9
10
|
import type { SSRTemplateCompiled } from './templates/templates';
|
package/lib/webpack/base.js
CHANGED
|
@@ -88,9 +88,9 @@ async function createBaseConfig({ props, isServer, minify, }) {
|
|
|
88
88
|
},
|
|
89
89
|
devtool: isProd ? undefined : 'eval-cheap-module-source-map',
|
|
90
90
|
resolve: {
|
|
91
|
-
unsafeCache: false,
|
|
91
|
+
unsafeCache: false, // Not enabled, does not seem to improve perf much
|
|
92
92
|
extensions: ['.wasm', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
|
|
93
|
-
symlinks: true,
|
|
93
|
+
symlinks: true, // See https://github.com/facebook/docusaurus/issues/3272
|
|
94
94
|
roots: [
|
|
95
95
|
// Allow resolution of url("/fonts/xyz.ttf") by webpack
|
|
96
96
|
// See https://webpack.js.org/configuration/resolve/#resolveroots
|
package/lib/webpack/utils.d.ts
CHANGED
|
@@ -18,9 +18,9 @@ export declare function getBabelOptions({ isServer, babelOptions, }?: {
|
|
|
18
18
|
isServer?: boolean;
|
|
19
19
|
babelOptions?: TransformOptions | string;
|
|
20
20
|
}): TransformOptions;
|
|
21
|
-
export declare const getCustomizableJSLoader: (jsLoader?:
|
|
21
|
+
export declare const getCustomizableJSLoader: (jsLoader?: 'babel' | ((isServer: boolean) => RuleSetRule)) => ({ isServer, babelOptions, }: {
|
|
22
22
|
isServer: boolean;
|
|
23
|
-
babelOptions?:
|
|
23
|
+
babelOptions?: TransformOptions | string;
|
|
24
24
|
}) => RuleSetRule;
|
|
25
25
|
/**
|
|
26
26
|
* Helper function to modify webpack config
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@docusaurus/core",
|
|
3
3
|
"description": "Easy to Maintain Open Source Documentation Websites",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -43,14 +43,12 @@
|
|
|
43
43
|
"@babel/runtime": "^7.22.6",
|
|
44
44
|
"@babel/runtime-corejs3": "^7.22.6",
|
|
45
45
|
"@babel/traverse": "^7.22.8",
|
|
46
|
-
"@docusaurus/cssnano-preset": "3.
|
|
47
|
-
"@docusaurus/logger": "3.
|
|
48
|
-
"@docusaurus/mdx-loader": "3.
|
|
49
|
-
"@docusaurus/
|
|
50
|
-
"@docusaurus/utils": "3.
|
|
51
|
-
"@docusaurus/utils-
|
|
52
|
-
"@docusaurus/utils-validation": "3.2.1",
|
|
53
|
-
"@svgr/webpack": "^6.5.1",
|
|
46
|
+
"@docusaurus/cssnano-preset": "3.3.0",
|
|
47
|
+
"@docusaurus/logger": "3.3.0",
|
|
48
|
+
"@docusaurus/mdx-loader": "3.3.0",
|
|
49
|
+
"@docusaurus/utils": "3.3.0",
|
|
50
|
+
"@docusaurus/utils-common": "3.3.0",
|
|
51
|
+
"@docusaurus/utils-validation": "3.3.0",
|
|
54
52
|
"autoprefixer": "^10.4.14",
|
|
55
53
|
"babel-loader": "^9.1.3",
|
|
56
54
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
@@ -64,8 +62,8 @@
|
|
|
64
62
|
"copy-webpack-plugin": "^11.0.0",
|
|
65
63
|
"core-js": "^3.31.1",
|
|
66
64
|
"css-loader": "^6.8.1",
|
|
67
|
-
"css-minimizer-webpack-plugin": "^
|
|
68
|
-
"cssnano": "^
|
|
65
|
+
"css-minimizer-webpack-plugin": "^5.0.1",
|
|
66
|
+
"cssnano": "^6.1.2",
|
|
69
67
|
"del": "^6.1.1",
|
|
70
68
|
"detect-port": "^1.5.1",
|
|
71
69
|
"escape-html": "^1.0.3",
|
|
@@ -85,7 +83,7 @@
|
|
|
85
83
|
"prompts": "^2.4.2",
|
|
86
84
|
"react-dev-utils": "^12.0.1",
|
|
87
85
|
"react-helmet-async": "^1.3.0",
|
|
88
|
-
"react-loadable": "npm:@docusaurus/react-loadable@
|
|
86
|
+
"react-loadable": "npm:@docusaurus/react-loadable@6.0.0",
|
|
89
87
|
"react-loadable-ssr-addon-v5-slorber": "^1.0.1",
|
|
90
88
|
"react-router": "^5.3.4",
|
|
91
89
|
"react-router-config": "^5.1.1",
|
|
@@ -105,8 +103,8 @@
|
|
|
105
103
|
"webpackbar": "^5.0.2"
|
|
106
104
|
},
|
|
107
105
|
"devDependencies": {
|
|
108
|
-
"@docusaurus/module-type-aliases": "3.
|
|
109
|
-
"@docusaurus/types": "3.
|
|
106
|
+
"@docusaurus/module-type-aliases": "3.3.0",
|
|
107
|
+
"@docusaurus/types": "3.3.0",
|
|
110
108
|
"@total-typescript/shoehorn": "^0.1.2",
|
|
111
109
|
"@types/detect-port": "^1.3.3",
|
|
112
110
|
"@types/react-dom": "^18.2.7",
|
|
@@ -126,5 +124,5 @@
|
|
|
126
124
|
"engines": {
|
|
127
125
|
"node": ">=18.0"
|
|
128
126
|
},
|
|
129
|
-
"gitHead": "
|
|
127
|
+
"gitHead": "2ec4e078b5ca0c57f2cc04f2fe564d524bb5e858"
|
|
130
128
|
}
|