@docusaurus/core 3.1.0 → 3.2.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 +10 -5
- package/lib/client/BrokenLinksContext.js +2 -2
- package/lib/client/exports/Link.js +10 -2
- package/lib/client/{serverRenderer.d.ts → renderToHtml.d.ts} +1 -1
- package/lib/client/{serverRenderer.js → renderToHtml.js} +1 -1
- package/lib/client/serverEntry.d.ts +3 -4
- package/lib/client/serverEntry.js +12 -113
- package/lib/client/theme-fallback/Error/index.js +22 -8
- package/lib/commands/build.d.ts +3 -3
- package/lib/commands/build.js +133 -112
- package/lib/commands/deploy.d.ts +2 -2
- package/lib/commands/deploy.js +3 -3
- package/lib/commands/external.js +2 -2
- package/lib/commands/serve.d.ts +2 -2
- package/lib/commands/serve.js +4 -4
- package/lib/commands/{start.d.ts → start/start.d.ts} +3 -3
- package/lib/commands/start/start.js +47 -0
- package/lib/commands/start/utils.d.ts +31 -0
- package/lib/commands/start/utils.js +87 -0
- package/lib/commands/start/watcher.d.ts +42 -0
- package/lib/commands/start/watcher.js +78 -0
- package/lib/commands/start/webpack.d.ts +15 -0
- package/lib/commands/start/webpack.js +133 -0
- package/lib/commands/swizzle/common.d.ts +1 -0
- package/lib/commands/swizzle/common.js +1 -0
- package/lib/commands/swizzle/context.js +2 -2
- package/lib/commands/swizzle/index.js +32 -3
- package/lib/commands/writeHeadingIds.js +2 -2
- package/lib/commands/writeTranslations.d.ts +2 -2
- package/lib/commands/writeTranslations.js +3 -3
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/server/brokenLinks.js +126 -39
- package/lib/server/clientModules.d.ts +1 -1
- package/lib/server/clientModules.js +3 -3
- package/lib/server/codegen/codegen.d.ts +20 -0
- package/lib/server/codegen/codegen.js +65 -0
- package/lib/server/codegen/codegenRoutes.d.ts +49 -0
- package/lib/server/codegen/codegenRoutes.js +190 -0
- package/lib/server/configValidation.js +6 -4
- package/lib/server/getHostPort.js +4 -1
- package/lib/server/i18n.d.ts +2 -2
- package/lib/server/i18n.js +20 -2
- package/lib/server/plugins/actions.d.ts +19 -0
- package/lib/server/plugins/actions.js +62 -0
- package/lib/server/plugins/init.js +3 -3
- package/lib/server/plugins/plugins.d.ts +21 -0
- package/lib/server/plugins/plugins.js +188 -0
- package/lib/server/plugins/pluginsUtils.d.ts +16 -0
- package/lib/server/plugins/pluginsUtils.js +75 -0
- package/lib/server/plugins/routeConfig.d.ts +1 -1
- package/lib/server/plugins/routeConfig.js +4 -4
- package/lib/server/plugins/synthetic.d.ts +3 -3
- package/lib/server/plugins/synthetic.js +0 -2
- package/lib/server/routes.d.ts +3 -45
- package/lib/server/routes.js +21 -165
- package/lib/server/{index.d.ts → site.d.ts} +12 -5
- package/lib/server/site.js +164 -0
- package/lib/server/siteMetadata.d.ts +5 -4
- package/lib/server/siteMetadata.js +14 -10
- package/lib/server/translations/translations.d.ts +9 -2
- package/lib/server/translations/translations.js +21 -4
- package/lib/server/utils.d.ts +0 -2
- package/lib/server/utils.js +1 -9
- package/lib/ssg.d.ts +31 -0
- package/lib/ssg.js +167 -0
- package/lib/templates/templates.d.ts +28 -0
- package/lib/templates/templates.js +63 -0
- package/lib/utils.d.ts +9 -0
- package/lib/utils.js +78 -0
- package/lib/webpack/base.d.ts +5 -1
- package/lib/webpack/base.js +4 -6
- package/lib/webpack/client.d.ts +15 -1
- package/lib/webpack/client.js +78 -23
- package/lib/webpack/minification.d.ts +8 -0
- package/lib/webpack/minification.js +97 -0
- package/lib/webpack/server.d.ts +5 -3
- package/lib/webpack/server.js +41 -50
- package/lib/webpack/utils.d.ts +13 -4
- package/lib/webpack/utils.js +27 -83
- package/package.json +13 -11
- package/lib/commands/start.js +0 -212
- package/lib/server/index.js +0 -154
- package/lib/server/plugins/index.d.ts +0 -18
- package/lib/server/plugins/index.js +0 -106
- /package/lib/{webpack/templates/index.html.template.ejs → templates/dev.html.template.ejs} +0 -0
- /package/lib/{webpack/templates → templates}/ssr.html.template.d.ts +0 -0
- /package/lib/{webpack/templates → templates}/ssr.html.template.js +0 -0
|
@@ -10,7 +10,7 @@ exports.writeTranslations = void 0;
|
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
12
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
13
|
-
const
|
|
13
|
+
const site_1 = require("../server/site");
|
|
14
14
|
const init_1 = require("../server/plugins/init");
|
|
15
15
|
const translations_1 = require("../server/translations/translations");
|
|
16
16
|
const translationsExtractor_1 = require("../server/translations/translationsExtractor");
|
|
@@ -55,7 +55,7 @@ async function writePluginTranslationFiles({ localizationDir, plugin, options, }
|
|
|
55
55
|
}
|
|
56
56
|
async function writeTranslations(siteDirParam = '.', options = {}) {
|
|
57
57
|
const siteDir = await fs_extra_1.default.realpath(siteDirParam);
|
|
58
|
-
const context = await (0,
|
|
58
|
+
const context = await (0, site_1.loadContext)({
|
|
59
59
|
siteDir,
|
|
60
60
|
config: options.config,
|
|
61
61
|
locale: options.locale,
|
|
@@ -72,7 +72,7 @@ Available locales are: ${context.i18n.locales.join(',')}.`);
|
|
|
72
72
|
babelOptions: await (0, utils_1.getCustomBabelConfigFilePath)(siteDir),
|
|
73
73
|
});
|
|
74
74
|
const extractedCodeTranslations = await (0, translationsExtractor_1.extractSiteSourceCodeTranslations)(siteDir, plugins, babelOptions, await getExtraSourceCodeFilePaths());
|
|
75
|
-
const defaultCodeMessages = await (0, translations_1.
|
|
75
|
+
const defaultCodeMessages = await (0, translations_1.loadPluginsDefaultCodeTranslationMessages)(plugins);
|
|
76
76
|
const codeTranslations = (0, translations_1.applyDefaultCodeTranslations)({
|
|
77
77
|
extractedCodeTranslations,
|
|
78
78
|
defaultCodeMessages,
|
package/lib/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { clear } from './commands/clear';
|
|
|
9
9
|
export { deploy } from './commands/deploy';
|
|
10
10
|
export { externalCommand } from './commands/external';
|
|
11
11
|
export { serve } from './commands/serve';
|
|
12
|
-
export { start } from './commands/start';
|
|
12
|
+
export { start } from './commands/start/start';
|
|
13
13
|
export { swizzle } from './commands/swizzle';
|
|
14
14
|
export { writeHeadingIds } from './commands/writeHeadingIds';
|
|
15
15
|
export { writeTranslations } from './commands/writeTranslations';
|
package/lib/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var external_1 = require("./commands/external");
|
|
|
17
17
|
Object.defineProperty(exports, "externalCommand", { enumerable: true, get: function () { return external_1.externalCommand; } });
|
|
18
18
|
var serve_1 = require("./commands/serve");
|
|
19
19
|
Object.defineProperty(exports, "serve", { enumerable: true, get: function () { return serve_1.serve; } });
|
|
20
|
-
var start_1 = require("./commands/start");
|
|
20
|
+
var start_1 = require("./commands/start/start");
|
|
21
21
|
Object.defineProperty(exports, "start", { enumerable: true, get: function () { return start_1.start; } });
|
|
22
22
|
var swizzle_1 = require("./commands/swizzle");
|
|
23
23
|
Object.defineProperty(exports, "swizzle", { enumerable: true, get: function () { return swizzle_1.swizzle; } });
|
|
@@ -12,17 +12,59 @@ const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
|
12
12
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
13
13
|
const react_router_config_1 = require("react-router-config");
|
|
14
14
|
const utils_1 = require("@docusaurus/utils");
|
|
15
|
-
const
|
|
16
|
-
function
|
|
17
|
-
//
|
|
15
|
+
const utils_common_1 = require("@docusaurus/utils-common");
|
|
16
|
+
function matchRoutes(routeConfig, pathname) {
|
|
17
|
+
// @ts-expect-error: React router types RouteConfig with an actual React
|
|
18
|
+
// component, but we load route components with string paths.
|
|
19
|
+
// We don't actually access component here, so it's fine.
|
|
20
|
+
return (0, react_router_config_1.matchRoutes)(routeConfig, pathname);
|
|
21
|
+
}
|
|
22
|
+
function createBrokenLinksHelper({ collectedLinks, routes, }) {
|
|
23
|
+
const validPathnames = new Set(collectedLinks.keys());
|
|
24
|
+
// IMPORTANT: this is an optimization
|
|
25
|
+
// See https://github.com/facebook/docusaurus/issues/9754
|
|
26
|
+
// Matching against the route array can be expensive
|
|
27
|
+
// If the route is already in the valid pathnames,
|
|
28
|
+
// we can avoid matching against it
|
|
29
|
+
const remainingRoutes = (function filterRoutes() {
|
|
30
|
+
// Goal: unit tests should behave the same with this enabled or disabled
|
|
31
|
+
const disableOptimization = false;
|
|
32
|
+
if (disableOptimization) {
|
|
33
|
+
return routes;
|
|
34
|
+
}
|
|
35
|
+
// We must consider the "exact" and "strict" match attribute
|
|
36
|
+
// We can only infer pre-validated pathnames from a route from exact routes
|
|
37
|
+
const [validPathnameRoutes, otherRoutes] = lodash_1.default.partition(routes, (route) => route.exact && validPathnames.has(route.path));
|
|
38
|
+
// If a route is non-strict (non-sensitive to trailing slashes)
|
|
39
|
+
// We must pre-validate all possible paths
|
|
40
|
+
validPathnameRoutes.forEach((validPathnameRoute) => {
|
|
41
|
+
if (!validPathnameRoute.strict) {
|
|
42
|
+
validPathnames.add((0, utils_common_1.addTrailingSlash)(validPathnameRoute.path));
|
|
43
|
+
validPathnames.add((0, utils_common_1.removeTrailingSlash)(validPathnameRoute.path));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
return otherRoutes;
|
|
47
|
+
})();
|
|
48
|
+
function isPathnameMatchingAnyRoute(pathname) {
|
|
49
|
+
if (matchRoutes(remainingRoutes, pathname).length > 0) {
|
|
50
|
+
// IMPORTANT: this is an optimization
|
|
51
|
+
// See https://github.com/facebook/docusaurus/issues/9754
|
|
52
|
+
// Large Docusaurus sites have many routes!
|
|
53
|
+
// We try to minimize calls to a possibly expensive matchRoutes function
|
|
54
|
+
validPathnames.add(pathname);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
18
59
|
function isPathBrokenLink(linkPath) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
60
|
+
const pathnames = [linkPath.pathname, decodeURI(linkPath.pathname)];
|
|
61
|
+
if (pathnames.some((p) => validPathnames.has(p))) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (pathnames.some(isPathnameMatchingAnyRoute)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
return true;
|
|
26
68
|
}
|
|
27
69
|
function isAnchorBrokenLink(linkPath) {
|
|
28
70
|
const { pathname, hash } = linkPath;
|
|
@@ -30,37 +72,50 @@ function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, })
|
|
|
30
72
|
if (hash === undefined) {
|
|
31
73
|
return false;
|
|
32
74
|
}
|
|
33
|
-
|
|
75
|
+
// Link has empty hash ("#", "/page#"...): we do not report it as broken
|
|
76
|
+
// Empty hashes are used for various weird reasons, by us and other users...
|
|
77
|
+
// See for example: https://github.com/facebook/docusaurus/pull/6003
|
|
78
|
+
if (hash === '') {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
const targetPage = collectedLinks.get(pathname) || collectedLinks.get(decodeURI(pathname));
|
|
34
82
|
// link with anchor to a page that does not exist (or did not collect any
|
|
35
83
|
// link/anchor) is considered as a broken anchor
|
|
36
84
|
if (!targetPage) {
|
|
37
85
|
return true;
|
|
38
86
|
}
|
|
39
|
-
// it's a broken anchor if the target page
|
|
40
|
-
|
|
41
|
-
|
|
87
|
+
// it's a not broken anchor if the anchor exists on the target page
|
|
88
|
+
if (targetPage.anchors.has(hash) ||
|
|
89
|
+
targetPage.anchors.has(decodeURIComponent(hash))) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return true;
|
|
42
93
|
}
|
|
43
|
-
|
|
94
|
+
return {
|
|
95
|
+
collectedLinks,
|
|
96
|
+
isPathBrokenLink,
|
|
97
|
+
isAnchorBrokenLink,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function getBrokenLinksForPage({ pagePath, helper, }) {
|
|
101
|
+
const pageData = helper.collectedLinks.get(pagePath);
|
|
102
|
+
const brokenLinks = [];
|
|
103
|
+
pageData.links.forEach((link) => {
|
|
44
104
|
const linkPath = (0, utils_1.parseURLPath)(link, pagePath);
|
|
45
|
-
if (isPathBrokenLink(linkPath)) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
},
|
|
52
|
-
];
|
|
105
|
+
if (helper.isPathBrokenLink(linkPath)) {
|
|
106
|
+
brokenLinks.push({
|
|
107
|
+
link,
|
|
108
|
+
resolvedLink: (0, utils_1.serializeURLPath)(linkPath),
|
|
109
|
+
anchor: false,
|
|
110
|
+
});
|
|
53
111
|
}
|
|
54
|
-
if (isAnchorBrokenLink(linkPath)) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
];
|
|
112
|
+
else if (helper.isAnchorBrokenLink(linkPath)) {
|
|
113
|
+
brokenLinks.push({
|
|
114
|
+
link,
|
|
115
|
+
resolvedLink: (0, utils_1.serializeURLPath)(linkPath),
|
|
116
|
+
anchor: true,
|
|
117
|
+
});
|
|
62
118
|
}
|
|
63
|
-
return [];
|
|
64
119
|
});
|
|
65
120
|
return brokenLinks;
|
|
66
121
|
}
|
|
@@ -72,17 +127,29 @@ function getBrokenLinksForPage({ collectedLinks, pagePath, pageLinks, routes, })
|
|
|
72
127
|
*/
|
|
73
128
|
function filterIntermediateRoutes(routesInput) {
|
|
74
129
|
const routesWithout404 = routesInput.filter((route) => route.path !== '*');
|
|
75
|
-
return (0,
|
|
130
|
+
return (0, utils_1.flattenRoutes)(routesWithout404);
|
|
76
131
|
}
|
|
77
132
|
function getBrokenLinks({ collectedLinks, routes, }) {
|
|
78
133
|
const filteredRoutes = filterIntermediateRoutes(routes);
|
|
79
|
-
|
|
134
|
+
const helper = createBrokenLinksHelper({
|
|
80
135
|
collectedLinks,
|
|
81
|
-
pageLinks: pageCollectedData.links,
|
|
82
|
-
pageAnchors: pageCollectedData.anchors,
|
|
83
|
-
pagePath,
|
|
84
136
|
routes: filteredRoutes,
|
|
85
|
-
})
|
|
137
|
+
});
|
|
138
|
+
const result = {};
|
|
139
|
+
collectedLinks.forEach((_unused, pagePath) => {
|
|
140
|
+
try {
|
|
141
|
+
result[pagePath] = getBrokenLinksForPage({
|
|
142
|
+
pagePath,
|
|
143
|
+
helper,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
catch (e) {
|
|
147
|
+
throw new Error(`Unable to get broken links for page ${pagePath}.`, {
|
|
148
|
+
cause: e,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return result;
|
|
86
153
|
}
|
|
87
154
|
function brokenLinkMessage(brokenLink) {
|
|
88
155
|
const showResolvedLink = brokenLink.link !== brokenLink.resolvedLink;
|
|
@@ -179,11 +246,31 @@ function reportBrokenLinks({ brokenLinks, onBrokenLinks, onBrokenAnchors, }) {
|
|
|
179
246
|
logger_1.default.report(onBrokenAnchors)(anchorErrorMessage);
|
|
180
247
|
}
|
|
181
248
|
}
|
|
249
|
+
// Users might use the useBrokenLinks() API in weird unexpected ways
|
|
250
|
+
// JS users might call "collectLink(undefined)" for example
|
|
251
|
+
// TS users might call "collectAnchor('#hash')" with/without #
|
|
252
|
+
// We clean/normalize the collected data to avoid obscure errors being thrown
|
|
253
|
+
// We also use optimized data structures for a faster algorithm
|
|
254
|
+
function normalizeCollectedLinks(collectedLinks) {
|
|
255
|
+
const result = new Map();
|
|
256
|
+
Object.entries(collectedLinks).forEach(([pathname, pageCollectedData]) => {
|
|
257
|
+
result.set(pathname, {
|
|
258
|
+
links: new Set(pageCollectedData.links.filter(lodash_1.default.isString)),
|
|
259
|
+
anchors: new Set(pageCollectedData.anchors
|
|
260
|
+
.filter(lodash_1.default.isString)
|
|
261
|
+
.map((anchor) => (anchor.startsWith('#') ? anchor.slice(1) : anchor))),
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
return result;
|
|
265
|
+
}
|
|
182
266
|
async function handleBrokenLinks({ collectedLinks, onBrokenLinks, onBrokenAnchors, routes, }) {
|
|
183
267
|
if (onBrokenLinks === 'ignore' && onBrokenAnchors === 'ignore') {
|
|
184
268
|
return;
|
|
185
269
|
}
|
|
186
|
-
const brokenLinks = getBrokenLinks({
|
|
270
|
+
const brokenLinks = getBrokenLinks({
|
|
271
|
+
routes,
|
|
272
|
+
collectedLinks: normalizeCollectedLinks(collectedLinks),
|
|
273
|
+
});
|
|
187
274
|
reportBrokenLinks({ brokenLinks, onBrokenLinks, onBrokenAnchors });
|
|
188
275
|
}
|
|
189
276
|
exports.handleBrokenLinks = handleBrokenLinks;
|
|
@@ -9,4 +9,4 @@ import type { LoadedPlugin } from '@docusaurus/types';
|
|
|
9
9
|
* Runs the `getClientModules` lifecycle. The returned file paths are all
|
|
10
10
|
* absolute.
|
|
11
11
|
*/
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function getAllClientModules(plugins: LoadedPlugin[]): string[];
|
|
@@ -6,15 +6,15 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.getAllClientModules = void 0;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
11
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
12
12
|
/**
|
|
13
13
|
* Runs the `getClientModules` lifecycle. The returned file paths are all
|
|
14
14
|
* absolute.
|
|
15
15
|
*/
|
|
16
|
-
function
|
|
16
|
+
function getAllClientModules(plugins) {
|
|
17
17
|
return plugins.flatMap((plugin) => plugin.getClientModules?.().map((p) => path_1.default.resolve(plugin.path, p)) ??
|
|
18
18
|
[]);
|
|
19
19
|
}
|
|
20
|
-
exports.
|
|
20
|
+
exports.getAllClientModules = getAllClientModules;
|
|
@@ -0,0 +1,20 @@
|
|
|
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 { CodeTranslations, DocusaurusConfig, GlobalData, I18n, RouteConfig, SiteMetadata } from '@docusaurus/types';
|
|
8
|
+
type CodegenParams = {
|
|
9
|
+
generatedFilesDir: string;
|
|
10
|
+
siteConfig: DocusaurusConfig;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
clientModules: string[];
|
|
13
|
+
globalData: GlobalData;
|
|
14
|
+
i18n: I18n;
|
|
15
|
+
codeTranslations: CodeTranslations;
|
|
16
|
+
siteMetadata: SiteMetadata;
|
|
17
|
+
routes: RouteConfig[];
|
|
18
|
+
};
|
|
19
|
+
export declare function generateSiteFiles(params: CodegenParams): Promise<void>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
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.generateSiteFiles = void 0;
|
|
10
|
+
const utils_1 = require("@docusaurus/utils");
|
|
11
|
+
const codegenRoutes_1 = require("./codegenRoutes");
|
|
12
|
+
function genWarning({ generatedFilesDir }) {
|
|
13
|
+
return (0, utils_1.generate)(generatedFilesDir,
|
|
14
|
+
// cSpell:ignore DONT
|
|
15
|
+
'DONT-EDIT-THIS-FOLDER', `This folder stores temp files that Docusaurus' client bundler accesses.
|
|
16
|
+
|
|
17
|
+
DO NOT hand-modify files in this folder because they will be overwritten in the
|
|
18
|
+
next build. You can clear all build artifacts (including this folder) with the
|
|
19
|
+
\`docusaurus clear\` command.
|
|
20
|
+
`);
|
|
21
|
+
}
|
|
22
|
+
function genSiteConfig({ generatedFilesDir, siteConfig, }) {
|
|
23
|
+
return (0, utils_1.generate)(generatedFilesDir, `${utils_1.DEFAULT_CONFIG_FILE_NAME}.mjs`, `/*
|
|
24
|
+
* AUTOGENERATED - DON'T EDIT
|
|
25
|
+
* Your edits in this file will be overwritten in the next build!
|
|
26
|
+
* Modify the docusaurus.config.js file at your site's root instead.
|
|
27
|
+
*/
|
|
28
|
+
export default ${JSON.stringify(siteConfig, null, 2)};
|
|
29
|
+
`);
|
|
30
|
+
}
|
|
31
|
+
function genClientModules({ generatedFilesDir, clientModules, }) {
|
|
32
|
+
return (0, utils_1.generate)(generatedFilesDir, 'client-modules.js', `export default [
|
|
33
|
+
${clientModules
|
|
34
|
+
// Use `require()` because `import()` is async but client modules can have CSS
|
|
35
|
+
// and the order matters for loading CSS.
|
|
36
|
+
.map((clientModule) => ` require("${(0, utils_1.escapePath)(clientModule)}"),`)
|
|
37
|
+
.join('\n')}
|
|
38
|
+
];
|
|
39
|
+
`);
|
|
40
|
+
}
|
|
41
|
+
function genGlobalData({ generatedFilesDir, globalData, }) {
|
|
42
|
+
return (0, utils_1.generate)(generatedFilesDir, 'globalData.json', JSON.stringify(globalData, null, 2));
|
|
43
|
+
}
|
|
44
|
+
function genI18n({ generatedFilesDir, i18n, }) {
|
|
45
|
+
return (0, utils_1.generate)(generatedFilesDir, 'i18n.json', JSON.stringify(i18n, null, 2));
|
|
46
|
+
}
|
|
47
|
+
function genCodeTranslations({ generatedFilesDir, codeTranslations, }) {
|
|
48
|
+
return (0, utils_1.generate)(generatedFilesDir, 'codeTranslations.json', JSON.stringify(codeTranslations, null, 2));
|
|
49
|
+
}
|
|
50
|
+
function genSiteMetadata({ generatedFilesDir, siteMetadata, }) {
|
|
51
|
+
return (0, utils_1.generate)(generatedFilesDir, 'site-metadata.json', JSON.stringify(siteMetadata, null, 2));
|
|
52
|
+
}
|
|
53
|
+
async function generateSiteFiles(params) {
|
|
54
|
+
await Promise.all([
|
|
55
|
+
genWarning(params),
|
|
56
|
+
genClientModules(params),
|
|
57
|
+
genSiteConfig(params),
|
|
58
|
+
(0, codegenRoutes_1.generateRouteFiles)(params),
|
|
59
|
+
genGlobalData(params),
|
|
60
|
+
genSiteMetadata(params),
|
|
61
|
+
genI18n(params),
|
|
62
|
+
genCodeTranslations(params),
|
|
63
|
+
]);
|
|
64
|
+
}
|
|
65
|
+
exports.generateSiteFiles = generateSiteFiles;
|
|
@@ -0,0 +1,49 @@
|
|
|
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 { RouteConfig, RouteChunkNames } from '@docusaurus/types';
|
|
8
|
+
type RoutesCode = {
|
|
9
|
+
/** Serialized routes config that can be directly emitted into temp file. */
|
|
10
|
+
routesConfig: string;
|
|
11
|
+
/** @see {ChunkNames} */
|
|
12
|
+
routesChunkNames: RouteChunkNames;
|
|
13
|
+
/**
|
|
14
|
+
* A map from chunk name to module paths. Module paths would have backslash
|
|
15
|
+
* escaped already, so they can be directly printed.
|
|
16
|
+
*/
|
|
17
|
+
registry: {
|
|
18
|
+
[chunkName: string]: string;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Generates a unique chunk name that can be used in the chunk registry.
|
|
23
|
+
*
|
|
24
|
+
* @param modulePath A path to generate chunk name from. The actual value has no
|
|
25
|
+
* semantic significance.
|
|
26
|
+
* @param prefix A prefix to append to the chunk name, to avoid name clash.
|
|
27
|
+
* @param preferredName Chunk names default to `modulePath`, and this can supply
|
|
28
|
+
* a more human-readable name.
|
|
29
|
+
* @param shortId When `true`, the chunk name would only be a hash without any
|
|
30
|
+
* other characters. Useful for bundle size. Defaults to `true` in production.
|
|
31
|
+
*/
|
|
32
|
+
export declare function genChunkName(modulePath: string, prefix?: string, preferredName?: string, shortId?: boolean): string;
|
|
33
|
+
/**
|
|
34
|
+
* Routes are prepared into three temp files:
|
|
35
|
+
*
|
|
36
|
+
* - `routesConfig`, the route config passed to react-router. This file is kept
|
|
37
|
+
* minimal, because it can't be code-splitted.
|
|
38
|
+
* - `routesChunkNames`, a mapping from route paths (hashed) to code-splitted
|
|
39
|
+
* chunk names.
|
|
40
|
+
* - `registry`, a mapping from chunk names to options for react-loadable.
|
|
41
|
+
*/
|
|
42
|
+
export declare function generateRoutesCode(routeConfigs: RouteConfig[]): RoutesCode;
|
|
43
|
+
type GenerateRouteFilesParams = {
|
|
44
|
+
generatedFilesDir: string;
|
|
45
|
+
routes: RouteConfig[];
|
|
46
|
+
baseUrl: string;
|
|
47
|
+
};
|
|
48
|
+
export declare function generateRouteFiles({ generatedFilesDir, routes, }: GenerateRouteFilesParams): Promise<void>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,190 @@
|
|
|
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.generateRouteFiles = exports.generateRoutesCode = exports.genChunkName = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const querystring_1 = tslib_1.__importDefault(require("querystring"));
|
|
12
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
13
|
+
const utils_1 = require("@docusaurus/utils");
|
|
14
|
+
/** Indents every line of `str` by one level. */
|
|
15
|
+
function indent(str) {
|
|
16
|
+
return ` ${str.replace(/\n/g, `\n `)}`;
|
|
17
|
+
}
|
|
18
|
+
const chunkNameCache = new Map();
|
|
19
|
+
const chunkNameCount = new Map();
|
|
20
|
+
/**
|
|
21
|
+
* Generates a unique chunk name that can be used in the chunk registry.
|
|
22
|
+
*
|
|
23
|
+
* @param modulePath A path to generate chunk name from. The actual value has no
|
|
24
|
+
* semantic significance.
|
|
25
|
+
* @param prefix A prefix to append to the chunk name, to avoid name clash.
|
|
26
|
+
* @param preferredName Chunk names default to `modulePath`, and this can supply
|
|
27
|
+
* a more human-readable name.
|
|
28
|
+
* @param shortId When `true`, the chunk name would only be a hash without any
|
|
29
|
+
* other characters. Useful for bundle size. Defaults to `true` in production.
|
|
30
|
+
*/
|
|
31
|
+
function genChunkName(modulePath, prefix, preferredName, shortId = process.env.NODE_ENV === 'production') {
|
|
32
|
+
let chunkName = chunkNameCache.get(modulePath);
|
|
33
|
+
if (!chunkName) {
|
|
34
|
+
if (shortId) {
|
|
35
|
+
chunkName = (0, utils_1.simpleHash)(modulePath, 8);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
let str = modulePath;
|
|
39
|
+
if (preferredName) {
|
|
40
|
+
const shortHash = (0, utils_1.simpleHash)(modulePath, 3);
|
|
41
|
+
str = `${preferredName}${shortHash}`;
|
|
42
|
+
}
|
|
43
|
+
const name = (0, utils_1.docuHash)(str);
|
|
44
|
+
chunkName = prefix ? `${prefix}---${name}` : name;
|
|
45
|
+
}
|
|
46
|
+
const seenCount = (chunkNameCount.get(chunkName) ?? 0) + 1;
|
|
47
|
+
if (seenCount > 1) {
|
|
48
|
+
chunkName += seenCount.toString(36);
|
|
49
|
+
}
|
|
50
|
+
chunkNameCache.set(modulePath, chunkName);
|
|
51
|
+
chunkNameCount.set(chunkName, seenCount);
|
|
52
|
+
}
|
|
53
|
+
return chunkName;
|
|
54
|
+
}
|
|
55
|
+
exports.genChunkName = genChunkName;
|
|
56
|
+
/**
|
|
57
|
+
* Takes a piece of route config, and serializes it into raw JS code. The shape
|
|
58
|
+
* is the same as react-router's `RouteConfig`. Formatting is similar to
|
|
59
|
+
* `JSON.stringify` but without all the quotes.
|
|
60
|
+
*/
|
|
61
|
+
function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeStrings, props, }) {
|
|
62
|
+
const parts = [
|
|
63
|
+
`path: '${routePath}'`,
|
|
64
|
+
`component: ComponentCreator('${routePath}', '${routeHash}')`,
|
|
65
|
+
];
|
|
66
|
+
if (exact) {
|
|
67
|
+
parts.push(`exact: true`);
|
|
68
|
+
}
|
|
69
|
+
if (subroutesCodeStrings) {
|
|
70
|
+
parts.push(`routes: [
|
|
71
|
+
${indent(subroutesCodeStrings.join(',\n'))}
|
|
72
|
+
]`);
|
|
73
|
+
}
|
|
74
|
+
Object.entries(props).forEach(([propName, propValue]) => {
|
|
75
|
+
const isIdentifier = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(propName);
|
|
76
|
+
const key = isIdentifier ? propName : JSON.stringify(propName);
|
|
77
|
+
parts.push(`${key}: ${JSON.stringify(propValue)}`);
|
|
78
|
+
});
|
|
79
|
+
return `{
|
|
80
|
+
${indent(parts.join(',\n'))}
|
|
81
|
+
}`;
|
|
82
|
+
}
|
|
83
|
+
const isModule = (value) => typeof value === 'string' ||
|
|
84
|
+
(typeof value === 'object' &&
|
|
85
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
86
|
+
!!value?.__import);
|
|
87
|
+
/**
|
|
88
|
+
* Takes a {@link Module} (which is nothing more than a path plus some metadata
|
|
89
|
+
* like query) and returns the string path it represents.
|
|
90
|
+
*/
|
|
91
|
+
function getModulePath(target) {
|
|
92
|
+
if (typeof target === 'string') {
|
|
93
|
+
return target;
|
|
94
|
+
}
|
|
95
|
+
const queryStr = target.query ? `?${querystring_1.default.stringify(target.query)}` : '';
|
|
96
|
+
return `${target.path}${queryStr}`;
|
|
97
|
+
}
|
|
98
|
+
function genChunkNames(routeModule, prefix, name, res) {
|
|
99
|
+
if (isModule(routeModule)) {
|
|
100
|
+
// This is a leaf node, no need to recurse
|
|
101
|
+
const modulePath = getModulePath(routeModule);
|
|
102
|
+
const chunkName = genChunkName(modulePath, prefix, name);
|
|
103
|
+
res.registry[chunkName] = (0, utils_1.escapePath)(modulePath);
|
|
104
|
+
return chunkName;
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(routeModule)) {
|
|
107
|
+
return routeModule.map((val, index) => genChunkNames(val, `${index}`, name, res));
|
|
108
|
+
}
|
|
109
|
+
return lodash_1.default.mapValues(routeModule, (v, key) => genChunkNames(v, key, name, res));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* This is the higher level overview of route code generation. For each route
|
|
113
|
+
* config node, it returns the node's serialized form, and mutates `registry`,
|
|
114
|
+
* `routesPaths`, and `routesChunkNames` accordingly.
|
|
115
|
+
*/
|
|
116
|
+
function genRouteCode(routeConfig, res) {
|
|
117
|
+
const { path: routePath, component, modules = {}, context, routes: subroutes, priority, exact, metadata, ...props } = routeConfig;
|
|
118
|
+
if (typeof routePath !== 'string' || !component) {
|
|
119
|
+
throw new Error(`Invalid route config: path must be a string and component is required.
|
|
120
|
+
${JSON.stringify(routeConfig)}`);
|
|
121
|
+
}
|
|
122
|
+
const routeHash = (0, utils_1.simpleHash)(JSON.stringify(routeConfig), 3);
|
|
123
|
+
res.routesChunkNames[`${routePath}-${routeHash}`] = {
|
|
124
|
+
// Avoid clash with a prop called "component"
|
|
125
|
+
...genChunkNames({ __comp: component }, 'component', component, res),
|
|
126
|
+
...(context &&
|
|
127
|
+
genChunkNames({ __context: context }, 'context', routePath, res)),
|
|
128
|
+
...genChunkNames(modules, 'module', routePath, res),
|
|
129
|
+
};
|
|
130
|
+
return serializeRouteConfig({
|
|
131
|
+
routePath: routePath.replace(/'/g, "\\'"),
|
|
132
|
+
routeHash,
|
|
133
|
+
subroutesCodeStrings: subroutes?.map((r) => genRouteCode(r, res)),
|
|
134
|
+
exact,
|
|
135
|
+
props,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Routes are prepared into three temp files:
|
|
140
|
+
*
|
|
141
|
+
* - `routesConfig`, the route config passed to react-router. This file is kept
|
|
142
|
+
* minimal, because it can't be code-splitted.
|
|
143
|
+
* - `routesChunkNames`, a mapping from route paths (hashed) to code-splitted
|
|
144
|
+
* chunk names.
|
|
145
|
+
* - `registry`, a mapping from chunk names to options for react-loadable.
|
|
146
|
+
*/
|
|
147
|
+
function generateRoutesCode(routeConfigs) {
|
|
148
|
+
const res = {
|
|
149
|
+
// To be written by `genRouteCode`
|
|
150
|
+
routesConfig: '',
|
|
151
|
+
routesChunkNames: {},
|
|
152
|
+
registry: {},
|
|
153
|
+
};
|
|
154
|
+
// `genRouteCode` would mutate `res`
|
|
155
|
+
const routeConfigSerialized = routeConfigs
|
|
156
|
+
.map((r) => genRouteCode(r, res))
|
|
157
|
+
.join(',\n');
|
|
158
|
+
res.routesConfig = `import React from 'react';
|
|
159
|
+
import ComponentCreator from '@docusaurus/ComponentCreator';
|
|
160
|
+
|
|
161
|
+
export default [
|
|
162
|
+
${indent(routeConfigSerialized)},
|
|
163
|
+
{
|
|
164
|
+
path: '*',
|
|
165
|
+
component: ComponentCreator('*'),
|
|
166
|
+
},
|
|
167
|
+
];
|
|
168
|
+
`;
|
|
169
|
+
return res;
|
|
170
|
+
}
|
|
171
|
+
exports.generateRoutesCode = generateRoutesCode;
|
|
172
|
+
const genRegistry = ({ generatedFilesDir, registry, }) => (0, utils_1.generate)(generatedFilesDir, 'registry.js', `export default {
|
|
173
|
+
${Object.entries(registry)
|
|
174
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
175
|
+
.map(([chunkName, modulePath]) =>
|
|
176
|
+
// modulePath is already escaped by escapePath
|
|
177
|
+
` "${chunkName}": [() => import(/* webpackChunkName: "${chunkName}" */ "${modulePath}"), "${modulePath}", require.resolveWeak("${modulePath}")],`)
|
|
178
|
+
.join('\n')}};
|
|
179
|
+
`);
|
|
180
|
+
const genRoutesChunkNames = ({ generatedFilesDir, routesChunkNames, }) => (0, utils_1.generate)(generatedFilesDir, 'routesChunkNames.json', JSON.stringify(routesChunkNames, null, 2));
|
|
181
|
+
const genRoutes = ({ generatedFilesDir, routesConfig, }) => (0, utils_1.generate)(generatedFilesDir, 'routes.js', routesConfig);
|
|
182
|
+
async function generateRouteFiles({ generatedFilesDir, routes, }) {
|
|
183
|
+
const { registry, routesChunkNames, routesConfig } = generateRoutesCode(routes);
|
|
184
|
+
await Promise.all([
|
|
185
|
+
genRegistry({ generatedFilesDir, registry }),
|
|
186
|
+
genRoutesChunkNames({ generatedFilesDir, routesChunkNames }),
|
|
187
|
+
genRoutes({ generatedFilesDir, routesConfig }),
|
|
188
|
+
]);
|
|
189
|
+
}
|
|
190
|
+
exports.generateRouteFiles = generateRouteFiles;
|
|
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
9
9
|
exports.validateConfig = exports.ConfigSchema = exports.DEFAULT_CONFIG = exports.DEFAULT_MARKDOWN_CONFIG = exports.DEFAULT_I18N_CONFIG = void 0;
|
|
10
10
|
const utils_1 = require("@docusaurus/utils");
|
|
11
11
|
const utils_validation_1 = require("@docusaurus/utils-validation");
|
|
12
|
+
const utils_common_1 = require("@docusaurus/utils-common");
|
|
12
13
|
const DEFAULT_I18N_LOCALE = 'en';
|
|
13
14
|
exports.DEFAULT_I18N_CONFIG = {
|
|
14
15
|
defaultLocale: DEFAULT_I18N_LOCALE,
|
|
@@ -51,12 +52,13 @@ exports.DEFAULT_CONFIG = {
|
|
|
51
52
|
markdown: exports.DEFAULT_MARKDOWN_CONFIG,
|
|
52
53
|
};
|
|
53
54
|
function createPluginSchema(theme) {
|
|
54
|
-
return utils_validation_1.Joi.alternatives()
|
|
55
|
+
return (utils_validation_1.Joi.alternatives()
|
|
55
56
|
.try(utils_validation_1.Joi.function(), utils_validation_1.Joi.array()
|
|
56
57
|
.ordered(utils_validation_1.Joi.function().required(), utils_validation_1.Joi.object().required())
|
|
57
58
|
.length(2), utils_validation_1.Joi.string(), utils_validation_1.Joi.array()
|
|
58
59
|
.ordered(utils_validation_1.Joi.string().required(), utils_validation_1.Joi.object().required())
|
|
59
60
|
.length(2), utils_validation_1.Joi.any().valid(false, null))
|
|
61
|
+
// @ts-expect-error: bad lib def, doesn't recognize an array of reports
|
|
60
62
|
.error((errors) => {
|
|
61
63
|
errors.forEach((error) => {
|
|
62
64
|
const validConfigExample = theme
|
|
@@ -85,7 +87,7 @@ ${validConfigExample}
|
|
|
85
87
|
`;
|
|
86
88
|
});
|
|
87
89
|
return errors;
|
|
88
|
-
});
|
|
90
|
+
}));
|
|
89
91
|
}
|
|
90
92
|
const PluginSchema = createPluginSchema(false);
|
|
91
93
|
const ThemeSchema = createPluginSchema(true);
|
|
@@ -127,7 +129,7 @@ const SiteUrlSchema = utils_validation_1.Joi.string()
|
|
|
127
129
|
catch {
|
|
128
130
|
return helpers.error('any.invalid');
|
|
129
131
|
}
|
|
130
|
-
return (0,
|
|
132
|
+
return (0, utils_common_1.removeTrailingSlash)(value);
|
|
131
133
|
})
|
|
132
134
|
.messages({
|
|
133
135
|
'any.invalid': '"{#value}" does not look like a valid URL. Make sure it has a protocol; for example, "https://example.com".',
|
|
@@ -140,7 +142,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
|
|
|
140
142
|
.alternatives()
|
|
141
143
|
.try(utils_validation_1.Joi.string().required().allow(''))
|
|
142
144
|
.required()
|
|
143
|
-
.custom((value) => (0,
|
|
145
|
+
.custom((value) => (0, utils_common_1.addLeadingSlash)((0, utils_common_1.addTrailingSlash)(value))),
|
|
144
146
|
baseUrlIssueBanner: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.baseUrlIssueBanner),
|
|
145
147
|
favicon: utils_validation_1.Joi.string().optional(),
|
|
146
148
|
title: utils_validation_1.Joi.string().required(),
|