@docusaurus/core 3.7.0 → 3.8.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/beforeCli.mjs +3 -3
- package/lib/client/App.js +7 -4
- package/lib/client/serverEntry.js +10 -4
- package/lib/client/serverHelmetUtils.d.ts +11 -0
- package/lib/client/serverHelmetUtils.js +39 -0
- package/lib/client/theme-fallback/ThemeProvider/index.d.ts +9 -0
- package/lib/client/theme-fallback/ThemeProvider/index.js +17 -0
- package/lib/commands/build/buildLocale.js +33 -10
- package/lib/commands/clear.js +4 -3
- package/lib/commands/deploy.js +70 -28
- package/lib/commands/serve.js +2 -2
- package/lib/commands/start/start.js +7 -4
- package/lib/commands/start/utils.js +17 -3
- package/lib/commands/start/webpack.js +1 -1
- package/lib/commands/utils/clearPath.d.ts +10 -0
- package/lib/commands/utils/clearPath.js +21 -0
- package/lib/commands/utils/legacy/evalSourceMapMiddleware.d.ts +2 -0
- package/lib/commands/utils/legacy/evalSourceMapMiddleware.js +57 -0
- package/lib/commands/utils/openBrowser/openBrowser.d.ts +10 -0
- package/lib/commands/utils/openBrowser/openBrowser.js +124 -0
- package/lib/commands/utils/openBrowser/openChrome.applescript +94 -0
- package/lib/server/configValidation.d.ts +3 -1
- package/lib/server/configValidation.js +45 -4
- package/lib/ssg/ssgEnv.d.ts +10 -0
- package/lib/ssg/ssgEnv.js +38 -0
- package/lib/ssg/ssgExecutor.js +121 -8
- package/lib/ssg/ssgGlobalResult.d.ts +12 -0
- package/lib/ssg/ssgGlobalResult.js +74 -0
- package/lib/ssg/ssgParams.d.ts +1 -0
- package/lib/ssg/ssgParams.js +1 -0
- package/lib/ssg/ssgRenderer.d.ts +29 -0
- package/lib/ssg/{ssg.js → ssgRenderer.js} +55 -90
- package/lib/ssg/ssgTemplate.js +6 -7
- package/lib/ssg/ssgUtils.d.ts +0 -1
- package/lib/ssg/ssgUtils.js +0 -9
- package/lib/ssg/ssgWorkerInline.d.ts +12 -0
- package/lib/ssg/ssgWorkerInline.js +17 -0
- package/lib/ssg/ssgWorkerThread.d.ts +13 -0
- package/lib/ssg/ssgWorkerThread.js +36 -0
- package/lib/webpack/base.js +76 -28
- package/lib/webpack/client.js +0 -3
- package/lib/webpack/plugins/BundlerCPUProfilerPlugin.d.ts +12 -0
- package/lib/webpack/plugins/BundlerCPUProfilerPlugin.js +41 -0
- package/package.json +14 -14
- package/lib/ssg/ssg.d.ts +0 -21
- package/lib/webpack/plugins/CleanWebpackPlugin.d.ts +0 -59
- package/lib/webpack/plugins/CleanWebpackPlugin.js +0 -177
|
@@ -0,0 +1,29 @@
|
|
|
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 { SSGParams } from './ssgParams';
|
|
8
|
+
import type { PageCollectedData } from '../common';
|
|
9
|
+
export type SSGSuccess = {
|
|
10
|
+
success: true;
|
|
11
|
+
pathname: string;
|
|
12
|
+
result: {
|
|
13
|
+
collectedData: PageCollectedData;
|
|
14
|
+
warnings: string[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export type SSGError = {
|
|
18
|
+
success: false;
|
|
19
|
+
pathname: string;
|
|
20
|
+
error: Error;
|
|
21
|
+
};
|
|
22
|
+
export type SSGResult = SSGSuccess | SSGError;
|
|
23
|
+
export type SSGRenderer = {
|
|
24
|
+
shutdown: () => Promise<void>;
|
|
25
|
+
renderPathnames: (pathnames: string[]) => Promise<SSGResult[]>;
|
|
26
|
+
};
|
|
27
|
+
export declare function loadSSGRenderer({ params, }: {
|
|
28
|
+
params: SSGParams;
|
|
29
|
+
}): Promise<SSGRenderer>;
|
|
@@ -6,13 +6,10 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
10
|
-
exports.printSSGWarnings = printSSGWarnings;
|
|
11
|
-
exports.generateStaticFiles = generateStaticFiles;
|
|
9
|
+
exports.loadSSGRenderer = loadSSGRenderer;
|
|
12
10
|
const tslib_1 = require("tslib");
|
|
13
11
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
14
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
15
|
-
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
16
13
|
// TODO eval is archived / unmaintained: https://github.com/pierrec/node-eval
|
|
17
14
|
// We should internalize/modernize it
|
|
18
15
|
const eval_1 = tslib_1.__importDefault(require("eval"));
|
|
@@ -20,11 +17,11 @@ const p_map_1 = tslib_1.__importDefault(require("p-map"));
|
|
|
20
17
|
const logger_1 = tslib_1.__importStar(require("@docusaurus/logger"));
|
|
21
18
|
const bundler_1 = require("@docusaurus/bundler");
|
|
22
19
|
const ssgTemplate_1 = require("./ssgTemplate");
|
|
20
|
+
const ssgEnv_1 = require("./ssgEnv");
|
|
23
21
|
const ssgUtils_1 = require("./ssgUtils");
|
|
24
22
|
const ssgNodeRequire_1 = require("./ssgNodeRequire");
|
|
25
23
|
async function loadAppRenderer({ serverBundlePath, }) {
|
|
26
24
|
const source = await logger_1.PerfLogger.async(`Load server bundle`, () => fs_extra_1.default.readFile(serverBundlePath));
|
|
27
|
-
logger_1.PerfLogger.log(`Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`);
|
|
28
25
|
const filename = path_1.default.basename(serverBundlePath);
|
|
29
26
|
const ssgRequire = (0, ssgNodeRequire_1.createSSGRequire)(serverBundlePath);
|
|
30
27
|
const globals = {
|
|
@@ -42,7 +39,7 @@ async function loadAppRenderer({ serverBundlePath, }) {
|
|
|
42
39
|
/* scope: */ globals,
|
|
43
40
|
/* includeGlobals: */ true));
|
|
44
41
|
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
|
|
45
|
-
throw new Error(`
|
|
42
|
+
throw new Error(`Docusaurus Bug: server bundle export from "${filename}" must be a function that renders the Docusaurus React app, not ${typeof serverEntry?.default}`);
|
|
46
43
|
}
|
|
47
44
|
async function shutdown() {
|
|
48
45
|
ssgRequire.cleanup();
|
|
@@ -52,44 +49,8 @@ async function loadAppRenderer({ serverBundlePath, }) {
|
|
|
52
49
|
shutdown,
|
|
53
50
|
};
|
|
54
51
|
}
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
// See https://github.com/facebook/docusaurus/pull/10554
|
|
58
|
-
// See https://github.com/swc-project/swc/discussions/9616#discussioncomment-10846201
|
|
59
|
-
if (process.env.DOCUSAURUS_IGNORE_SSG_WARNINGS === 'true') {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const ignoredWarnings = [
|
|
63
|
-
// TODO React/Docusaurus emit NULL chars, and minifier detects it
|
|
64
|
-
// see https://github.com/facebook/docusaurus/issues/9985
|
|
65
|
-
'Unexpected null character',
|
|
66
|
-
];
|
|
67
|
-
const keepWarning = (warning) => {
|
|
68
|
-
return !ignoredWarnings.some((iw) => warning.includes(iw));
|
|
69
|
-
};
|
|
70
|
-
const resultsWithWarnings = results
|
|
71
|
-
.map((result) => {
|
|
72
|
-
return {
|
|
73
|
-
...result,
|
|
74
|
-
warnings: result.warnings.filter(keepWarning),
|
|
75
|
-
};
|
|
76
|
-
})
|
|
77
|
-
.filter((result) => result.warnings.length > 0);
|
|
78
|
-
if (resultsWithWarnings.length) {
|
|
79
|
-
const message = `Docusaurus static site generation process emitted warnings for ${resultsWithWarnings.length} path${resultsWithWarnings.length ? 's' : ''}
|
|
80
|
-
This is non-critical and can be disabled with DOCUSAURUS_IGNORE_SSG_WARNINGS=true
|
|
81
|
-
Troubleshooting guide: https://github.com/facebook/docusaurus/discussions/10580
|
|
82
|
-
|
|
83
|
-
- ${resultsWithWarnings
|
|
84
|
-
.map((result) => `${logger_1.default.path(result.pathname)}:
|
|
85
|
-
- ${result.warnings.join('\n - ')}
|
|
86
|
-
`)
|
|
87
|
-
.join('\n- ')}`;
|
|
88
|
-
logger_1.default.warn(message);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async function generateStaticFiles({ pathnames, params, }) {
|
|
92
|
-
const [renderer, htmlMinifier, ssgTemplate] = await Promise.all([
|
|
52
|
+
async function loadSSGRenderer({ params, }) {
|
|
53
|
+
const [appRenderer, htmlMinifier, ssgTemplate] = await Promise.all([
|
|
93
54
|
logger_1.PerfLogger.async('Load App renderer', () => loadAppRenderer({
|
|
94
55
|
serverBundlePath: params.serverBundlePath,
|
|
95
56
|
})),
|
|
@@ -98,54 +59,49 @@ async function generateStaticFiles({ pathnames, params, }) {
|
|
|
98
59
|
})),
|
|
99
60
|
logger_1.PerfLogger.async('Compile SSG template', () => (0, ssgTemplate_1.compileSSGTemplate)(params.ssgTemplateContent)),
|
|
100
61
|
]);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
pathname,
|
|
116
|
-
result: null,
|
|
117
|
-
error: error,
|
|
118
|
-
warnings: [],
|
|
119
|
-
})), { concurrency: ssgUtils_1.SSGConcurrency });
|
|
120
|
-
await renderer.shutdown();
|
|
121
|
-
printSSGWarnings(results);
|
|
122
|
-
const [allSSGErrors, allSSGSuccesses] = lodash_1.default.partition(results, (result) => !!result.error);
|
|
123
|
-
if (allSSGErrors.length > 0) {
|
|
124
|
-
const message = `Docusaurus static site generation failed for ${allSSGErrors.length} path${allSSGErrors.length ? 's' : ''}:\n- ${allSSGErrors
|
|
125
|
-
.map((ssgError) => logger_1.default.path(ssgError.pathname))
|
|
126
|
-
.join('\n- ')}`;
|
|
127
|
-
// Note logging this error properly require using inspect(error,{depth})
|
|
128
|
-
// See https://github.com/nodejs/node/issues/51637
|
|
129
|
-
throw new Error(message, {
|
|
130
|
-
cause: new AggregateError(allSSGErrors.map((ssgError) => ssgError.error)),
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
const collectedData = lodash_1.default.chain(allSSGSuccesses)
|
|
134
|
-
.keyBy((success) => success.pathname)
|
|
135
|
-
.mapValues((ssgSuccess) => ssgSuccess.result.collectedData)
|
|
136
|
-
.value();
|
|
137
|
-
return { collectedData };
|
|
62
|
+
return {
|
|
63
|
+
renderPathnames: (pathnames) => {
|
|
64
|
+
return (0, p_map_1.default)(pathnames, async (pathname) => generateStaticFile({
|
|
65
|
+
pathname,
|
|
66
|
+
appRenderer,
|
|
67
|
+
params,
|
|
68
|
+
htmlMinifier,
|
|
69
|
+
ssgTemplate,
|
|
70
|
+
}), { concurrency: ssgEnv_1.SSGConcurrency });
|
|
71
|
+
},
|
|
72
|
+
shutdown: async () => {
|
|
73
|
+
await appRenderer.shutdown();
|
|
74
|
+
},
|
|
75
|
+
};
|
|
138
76
|
}
|
|
139
|
-
|
|
77
|
+
// We reduce the page collected data structure after the HTML file is written
|
|
78
|
+
// Some data (modules, metadata.internal) is only useful to create the HTML file
|
|
79
|
+
// It's not useful to aggregate that collected data in memory
|
|
80
|
+
// Keep this data structure as small as possible
|
|
81
|
+
// See https://github.com/facebook/docusaurus/pull/11162
|
|
82
|
+
function reduceCollectedData(pageCollectedData) {
|
|
83
|
+
// We re-create the object from scratch
|
|
84
|
+
// We absolutely want to avoid TS duck typing
|
|
85
|
+
return {
|
|
86
|
+
anchors: pageCollectedData.anchors,
|
|
87
|
+
metadata: {
|
|
88
|
+
public: pageCollectedData.metadata.public,
|
|
89
|
+
helmet: pageCollectedData.metadata.helmet,
|
|
90
|
+
},
|
|
91
|
+
links: pageCollectedData.links,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
async function generateStaticFile({ pathname, appRenderer, params, htmlMinifier, ssgTemplate, }) {
|
|
140
95
|
try {
|
|
141
96
|
// This only renders the app HTML
|
|
142
|
-
const
|
|
97
|
+
const appRenderResult = await appRenderer.render({
|
|
143
98
|
pathname,
|
|
99
|
+
v4RemoveLegacyPostBuildHeadAttribute: params.v4RemoveLegacyPostBuildHeadAttribute,
|
|
144
100
|
});
|
|
145
101
|
// This renders the full page HTML, including head tags...
|
|
146
102
|
const fullPageHtml = (0, ssgTemplate_1.renderSSGTemplate)({
|
|
147
103
|
params,
|
|
148
|
-
result,
|
|
104
|
+
result: appRenderResult,
|
|
149
105
|
ssgTemplate,
|
|
150
106
|
});
|
|
151
107
|
const minifierResult = await htmlMinifier.minify(fullPageHtml);
|
|
@@ -154,19 +110,28 @@ async function generateStaticFile({ pathname, renderer, params, htmlMinifier, ss
|
|
|
154
110
|
content: minifierResult.code,
|
|
155
111
|
params,
|
|
156
112
|
});
|
|
113
|
+
const collectedData = reduceCollectedData(appRenderResult.collectedData);
|
|
157
114
|
return {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
115
|
+
success: true,
|
|
116
|
+
pathname,
|
|
117
|
+
result: {
|
|
118
|
+
collectedData,
|
|
119
|
+
// As of today, only the html minifier can emit SSG warnings
|
|
120
|
+
warnings: minifierResult.warnings,
|
|
121
|
+
},
|
|
161
122
|
};
|
|
162
123
|
}
|
|
163
124
|
catch (errorUnknown) {
|
|
164
125
|
const error = errorUnknown;
|
|
165
126
|
const tips = getSSGErrorTips(error);
|
|
166
127
|
const message = logger_1.default.interpolate `Can't render static file for pathname path=${pathname}${tips ? `\n\n${tips}` : ''}`;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
128
|
+
return {
|
|
129
|
+
success: false,
|
|
130
|
+
pathname,
|
|
131
|
+
error: new Error(message, {
|
|
132
|
+
cause: error,
|
|
133
|
+
}),
|
|
134
|
+
};
|
|
170
135
|
}
|
|
171
136
|
}
|
|
172
137
|
function getSSGErrorTips(error) {
|
package/lib/ssg/ssgTemplate.js
CHANGED
|
@@ -35,15 +35,14 @@ function getScriptsAndStylesheets({ modules, manifest, }) {
|
|
|
35
35
|
}
|
|
36
36
|
function renderSSGTemplate({ params, result, ssgTemplate, }) {
|
|
37
37
|
const { baseUrl, headTags, preBodyTags, postBodyTags, manifest, noIndex, DOCUSAURUS_VERSION, } = params;
|
|
38
|
-
const { html: appHtml, collectedData: { modules,
|
|
38
|
+
const { html: appHtml, collectedData: { modules, metadata }, } = result;
|
|
39
39
|
const { scripts, stylesheets } = getScriptsAndStylesheets({ manifest, modules });
|
|
40
|
-
const htmlAttributes =
|
|
41
|
-
const bodyAttributes = helmet.bodyAttributes.toString();
|
|
40
|
+
const { htmlAttributes, bodyAttributes } = metadata.internal;
|
|
42
41
|
const metaStrings = [
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
metadata.internal.title,
|
|
43
|
+
metadata.internal.meta,
|
|
44
|
+
metadata.internal.link,
|
|
45
|
+
metadata.internal.script,
|
|
47
46
|
];
|
|
48
47
|
const metaAttributes = metaStrings.filter(Boolean);
|
|
49
48
|
const data = {
|
package/lib/ssg/ssgUtils.d.ts
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
import type { SSGParams } from './ssgParams';
|
|
8
|
-
export declare const SSGConcurrency: number;
|
|
9
8
|
export declare function generateHashRouterEntrypoint({ content, params, }: {
|
|
10
9
|
content: string;
|
|
11
10
|
params: SSGParams;
|
package/lib/ssg/ssgUtils.js
CHANGED
|
@@ -6,20 +6,11 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.SSGConcurrency = void 0;
|
|
10
9
|
exports.generateHashRouterEntrypoint = generateHashRouterEntrypoint;
|
|
11
10
|
exports.writeStaticFile = writeStaticFile;
|
|
12
11
|
const tslib_1 = require("tslib");
|
|
13
12
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
14
13
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
15
|
-
// Secret way to set SSR plugin concurrency option
|
|
16
|
-
// Waiting for feedback before documenting this officially?
|
|
17
|
-
exports.SSGConcurrency = process.env.DOCUSAURUS_SSR_CONCURRENCY
|
|
18
|
-
? parseInt(process.env.DOCUSAURUS_SSR_CONCURRENCY, 10)
|
|
19
|
-
: // Not easy to define a reasonable option default
|
|
20
|
-
// Will still be better than Infinity
|
|
21
|
-
// See also https://github.com/sindresorhus/p-map/issues/24
|
|
22
|
-
32;
|
|
23
14
|
function pathnameToFilename({ pathname, trailingSlash, }) {
|
|
24
15
|
const outputFileName = pathname.replace(/^[/\\]/, ''); // Remove leading slashes for webpack-dev-server
|
|
25
16
|
// Paths ending with .html are left untouched
|
|
@@ -0,0 +1,12 @@
|
|
|
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 SSGResult } from './ssgRenderer';
|
|
8
|
+
import type { SSGParams } from './ssgParams';
|
|
9
|
+
export declare function executeSSGInlineTask(arg: {
|
|
10
|
+
pathnames: string[];
|
|
11
|
+
params: SSGParams;
|
|
12
|
+
}): Promise<SSGResult[]>;
|
|
@@ -0,0 +1,17 @@
|
|
|
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.executeSSGInlineTask = executeSSGInlineTask;
|
|
10
|
+
const ssgRenderer_1 = require("./ssgRenderer");
|
|
11
|
+
// "inline" means in the current thread, not in a worker
|
|
12
|
+
async function executeSSGInlineTask(arg) {
|
|
13
|
+
const appRenderer = await (0, ssgRenderer_1.loadSSGRenderer)({ params: arg.params });
|
|
14
|
+
const ssgResults = appRenderer.renderPathnames(arg.pathnames);
|
|
15
|
+
await appRenderer.shutdown();
|
|
16
|
+
return ssgResults;
|
|
17
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
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 SSGResult } from './ssgRenderer.js';
|
|
8
|
+
export type SSGWorkerThreadTask = {
|
|
9
|
+
id: number;
|
|
10
|
+
pathnames: string[];
|
|
11
|
+
};
|
|
12
|
+
export default function executeSSGWorkerThreadTask(task: SSGWorkerThreadTask): Promise<SSGResult[]>;
|
|
13
|
+
export type ExecuteSSGWorkerThreadTask = typeof executeSSGWorkerThreadTask;
|
|
@@ -0,0 +1,36 @@
|
|
|
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.default = executeSSGWorkerThreadTask;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const node_worker_threads_1 = require("node:worker_threads");
|
|
12
|
+
const logger_1 = tslib_1.__importStar(require("@docusaurus/logger"));
|
|
13
|
+
const ssgRenderer_js_1 = require("./ssgRenderer.js");
|
|
14
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
15
|
+
const workerId = process?.__tinypool_state__?.workerId;
|
|
16
|
+
if (!workerId) {
|
|
17
|
+
throw new Error('SSG Worker Thread not executing in Tinypool context?');
|
|
18
|
+
}
|
|
19
|
+
const params = node_worker_threads_1.workerData?.[1]?.params;
|
|
20
|
+
if (!params) {
|
|
21
|
+
throw new Error(`SSG Worker Thread workerData params missing`);
|
|
22
|
+
}
|
|
23
|
+
const WorkerLogPrefix = `SSG Worker ${logger_1.default.name(workerId)}`;
|
|
24
|
+
// We only load once the SSG rendered (expensive), NOT once per worker task
|
|
25
|
+
// TODO check potential memory leak?
|
|
26
|
+
const appRendererPromise = logger_1.PerfLogger.async(`${WorkerLogPrefix} - Initialization`, () => (0, ssgRenderer_js_1.loadSSGRenderer)({
|
|
27
|
+
params,
|
|
28
|
+
}));
|
|
29
|
+
async function executeSSGWorkerThreadTask(task) {
|
|
30
|
+
const appRenderer = await appRendererPromise;
|
|
31
|
+
const ssgResults = await logger_1.PerfLogger.async(`${WorkerLogPrefix} - Task ${logger_1.default.name(task.id)} - Rendering ${logger_1.default.cyan(task.pathnames.length)} pathnames`, () => appRenderer.renderPathnames(task.pathnames));
|
|
32
|
+
// Afaik it's not needed to shutdown here,
|
|
33
|
+
// The thread pool destroys worker thread and releases worker thread memory
|
|
34
|
+
// await appRenderer.shutdown();
|
|
35
|
+
return ssgResults;
|
|
36
|
+
}
|
package/lib/webpack/base.js
CHANGED
|
@@ -16,6 +16,7 @@ const babel_1 = require("@docusaurus/babel");
|
|
|
16
16
|
const bundler_1 = require("@docusaurus/bundler");
|
|
17
17
|
const utils_1 = require("@docusaurus/utils");
|
|
18
18
|
const aliases_1 = require("./aliases");
|
|
19
|
+
const BundlerCPUProfilerPlugin_1 = require("./plugins/BundlerCPUProfilerPlugin");
|
|
19
20
|
const CSS_REGEX = /\.css$/i;
|
|
20
21
|
const CSS_MODULE_REGEX = /\.module\.css$/i;
|
|
21
22
|
exports.clientDir = path_1.default.join(__dirname, '..', 'client');
|
|
@@ -58,43 +59,72 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
|
|
|
58
59
|
const CSSExtractPlugin = await (0, bundler_1.getCSSExtractPlugin)({
|
|
59
60
|
currentBundler: props.currentBundler,
|
|
60
61
|
});
|
|
62
|
+
// Can we share the same cache across locales?
|
|
63
|
+
// Exploring that question at https://github.com/webpack/webpack/issues/13034
|
|
64
|
+
function getCacheName() {
|
|
65
|
+
return `${name}-${mode}-${props.i18n.currentLocale}`;
|
|
66
|
+
}
|
|
67
|
+
// When the version string changes, the cache is evicted
|
|
68
|
+
function getCacheVersion() {
|
|
69
|
+
// Because Webpack does not evict the cache on alias/swizzle changes,
|
|
70
|
+
// See https://github.com/webpack/webpack/issues/13627
|
|
71
|
+
const themeAliasesHash = (0, utils_1.md5Hash)(JSON.stringify(themeAliases));
|
|
72
|
+
return `${siteMetadata.docusaurusVersion}-${themeAliasesHash}`;
|
|
73
|
+
}
|
|
74
|
+
// When one of those modules/dependencies change (including transitive
|
|
75
|
+
// deps), cache is invalidated
|
|
76
|
+
function getCacheBuildDependencies() {
|
|
77
|
+
return [
|
|
78
|
+
__filename,
|
|
79
|
+
path_1.default.join(__dirname, isServer ? 'server.js' : 'client.js'),
|
|
80
|
+
// Docusaurus config changes can affect MDX/JSX compilation, so we'd
|
|
81
|
+
// rather evict the cache.
|
|
82
|
+
// See https://github.com/questdb/questdb.io/issues/493
|
|
83
|
+
siteConfigPath,
|
|
84
|
+
];
|
|
85
|
+
}
|
|
61
86
|
function getCache() {
|
|
87
|
+
// Use default: memory cache in dev, nothing in prod
|
|
88
|
+
// See https://rspack.dev/config/cache#cache
|
|
89
|
+
const disabledPersistentCacheValue = undefined;
|
|
90
|
+
if (process.env.DOCUSAURUS_NO_PERSISTENT_CACHE) {
|
|
91
|
+
return disabledPersistentCacheValue;
|
|
92
|
+
}
|
|
62
93
|
if (props.currentBundler.name === 'rspack') {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
94
|
+
if (props.siteConfig.future.experimental_faster.rspackPersistentCache) {
|
|
95
|
+
// Use cache: true + experiments.cache.type: "persistent"
|
|
96
|
+
// See https://rspack.dev/config/experiments#persistent-cache
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return disabledPersistentCacheValue;
|
|
101
|
+
}
|
|
67
102
|
}
|
|
68
103
|
return {
|
|
69
104
|
type: 'filesystem',
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
// name: `${name}-${mode}`,
|
|
73
|
-
name: `${name}-${mode}-${props.i18n.currentLocale}`,
|
|
74
|
-
// When version string changes, cache is evicted
|
|
75
|
-
version: [
|
|
76
|
-
siteMetadata.docusaurusVersion,
|
|
77
|
-
// Webpack does not evict the cache correctly on alias/swizzle change,
|
|
78
|
-
// so we force eviction.
|
|
79
|
-
// See https://github.com/webpack/webpack/issues/13627
|
|
80
|
-
(0, utils_1.md5Hash)(JSON.stringify(themeAliases)),
|
|
81
|
-
].join('-'),
|
|
82
|
-
// When one of those modules/dependencies change (including transitive
|
|
83
|
-
// deps), cache is invalidated
|
|
105
|
+
name: getCacheName(),
|
|
106
|
+
version: getCacheVersion(),
|
|
84
107
|
buildDependencies: {
|
|
85
|
-
config:
|
|
86
|
-
__filename,
|
|
87
|
-
path_1.default.join(__dirname, isServer ? 'server.js' : 'client.js'),
|
|
88
|
-
// Docusaurus config changes can affect MDX/JSX compilation, so we'd
|
|
89
|
-
// rather evict the cache.
|
|
90
|
-
// See https://github.com/questdb/questdb.io/issues/493
|
|
91
|
-
siteConfigPath,
|
|
92
|
-
],
|
|
108
|
+
config: getCacheBuildDependencies(),
|
|
93
109
|
},
|
|
94
110
|
};
|
|
95
111
|
}
|
|
96
112
|
function getExperiments() {
|
|
97
113
|
if (props.currentBundler.name === 'rspack') {
|
|
114
|
+
const PersistentCacheAttributes = process.env
|
|
115
|
+
.DOCUSAURUS_NO_PERSISTENT_CACHE
|
|
116
|
+
? {}
|
|
117
|
+
: {
|
|
118
|
+
cache: {
|
|
119
|
+
type: 'persistent',
|
|
120
|
+
// Rspack doesn't have "cache.name" like Webpack
|
|
121
|
+
// This is not ideal but work around is to merge name/version
|
|
122
|
+
// See https://github.com/web-infra-dev/rspack/pull/8920#issuecomment-2658938695
|
|
123
|
+
version: `${getCacheName()}-${getCacheVersion()}`,
|
|
124
|
+
buildDependencies: getCacheBuildDependencies(),
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
// TODO find a way to type this
|
|
98
128
|
return {
|
|
99
129
|
// This is mostly useful in dev
|
|
100
130
|
// See https://rspack.dev/config/experiments#experimentsincremental
|
|
@@ -105,6 +135,10 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
|
|
|
105
135
|
// See https://github.com/facebook/docusaurus/issues/10646
|
|
106
136
|
// @ts-expect-error: Rspack-only, not available in Webpack typedefs
|
|
107
137
|
incremental: !isProd && !process.env.DISABLE_RSPACK_INCREMENTAL,
|
|
138
|
+
// TODO re-enable later, there's an Rspack performance issue
|
|
139
|
+
// see https://github.com/facebook/docusaurus/pull/11178
|
|
140
|
+
parallelCodeSplitting: false,
|
|
141
|
+
...PersistentCacheAttributes,
|
|
108
142
|
};
|
|
109
143
|
}
|
|
110
144
|
return undefined;
|
|
@@ -162,7 +196,18 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
|
|
|
162
196
|
modules: ['node_modules', path_1.default.join(siteDir, 'node_modules')],
|
|
163
197
|
},
|
|
164
198
|
optimization: {
|
|
165
|
-
|
|
199
|
+
// The optimization.concatenateModules is expensive
|
|
200
|
+
// - On the server, it's not useful to run it at all
|
|
201
|
+
// - On the client, it leads to a ~3% JS assets total size decrease
|
|
202
|
+
// Let's keep it by default, but large sites may prefer faster builds
|
|
203
|
+
// See also https://github.com/facebook/docusaurus/pull/11176
|
|
204
|
+
concatenateModules: !isServer,
|
|
205
|
+
// The optimization.mergeDuplicateChunks is expensive
|
|
206
|
+
// - On the server, it's not useful to run it at all
|
|
207
|
+
// - On the client, we compared assets/js before/after and see 0 change
|
|
208
|
+
// `du -sk js-before js-after` => the JS assets have the exact same size
|
|
209
|
+
// See also https://github.com/facebook/docusaurus/pull/11176
|
|
210
|
+
mergeDuplicateChunks: false,
|
|
166
211
|
// Only minimize client bundle in production because server bundle is only
|
|
167
212
|
// used for static site generation
|
|
168
213
|
minimize: minimizeEnabled,
|
|
@@ -201,6 +246,7 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
|
|
|
201
246
|
module: {
|
|
202
247
|
rules: [
|
|
203
248
|
fileLoaderUtils.rules.images(),
|
|
249
|
+
fileLoaderUtils.rules.svgs(),
|
|
204
250
|
fileLoaderUtils.rules.fonts(),
|
|
205
251
|
fileLoaderUtils.rules.media(),
|
|
206
252
|
fileLoaderUtils.rules.otherAssets(),
|
|
@@ -252,6 +298,8 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
|
|
|
252
298
|
// for more reasoning
|
|
253
299
|
ignoreOrder: true,
|
|
254
300
|
}),
|
|
255
|
-
|
|
301
|
+
process.env.DOCUSAURUS_BUNDLER_CPU_PROFILE &&
|
|
302
|
+
new BundlerCPUProfilerPlugin_1.BundlerCPUProfilerPlugin(),
|
|
303
|
+
].filter(Boolean),
|
|
256
304
|
};
|
|
257
305
|
}
|
package/lib/webpack/client.js
CHANGED
|
@@ -17,7 +17,6 @@ const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plug
|
|
|
17
17
|
const bundler_1 = require("@docusaurus/bundler");
|
|
18
18
|
const base_1 = require("./base");
|
|
19
19
|
const ChunkAssetPlugin_1 = tslib_1.__importDefault(require("./plugins/ChunkAssetPlugin"));
|
|
20
|
-
const CleanWebpackPlugin_1 = tslib_1.__importDefault(require("./plugins/CleanWebpackPlugin"));
|
|
21
20
|
const ForceTerminatePlugin_1 = tslib_1.__importDefault(require("./plugins/ForceTerminatePlugin"));
|
|
22
21
|
const StaticDirectoriesCopyPlugin_1 = require("./plugins/StaticDirectoriesCopyPlugin");
|
|
23
22
|
async function createBaseClientConfig({ props, hydrate, minify, faster, configureWebpackUtils, }) {
|
|
@@ -108,8 +107,6 @@ async function createBuildClientConfig({ props, minify, faster, configureWebpack
|
|
|
108
107
|
}), {
|
|
109
108
|
plugins: [
|
|
110
109
|
new ForceTerminatePlugin_1.default(),
|
|
111
|
-
// Remove/clean build folders before building bundles.
|
|
112
|
-
new CleanWebpackPlugin_1.default({ verbose: false }),
|
|
113
110
|
// Visualize size of webpack output files with an interactive zoomable
|
|
114
111
|
// tree map.
|
|
115
112
|
bundleAnalyzer && new webpack_bundle_analyzer_1.BundleAnalyzerPlugin(),
|
|
@@ -0,0 +1,12 @@
|
|
|
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 { Compiler } from 'webpack';
|
|
8
|
+
export declare class BundlerCPUProfilerPlugin {
|
|
9
|
+
output: string;
|
|
10
|
+
constructor(output?: string);
|
|
11
|
+
apply(compiler: Compiler): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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.BundlerCPUProfilerPlugin = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const node_inspector_1 = tslib_1.__importDefault(require("node:inspector"));
|
|
12
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
13
|
+
// Bundle CPU profiling plugin, contributed by the Rspack team
|
|
14
|
+
// Can be opened in https://www.speedscope.app/
|
|
15
|
+
// See also https://github.com/jerrykingxyz/docusaurus/pull/1
|
|
16
|
+
// See also https://github.com/facebook/docusaurus/pull/10985
|
|
17
|
+
class BundlerCPUProfilerPlugin {
|
|
18
|
+
constructor(output) {
|
|
19
|
+
this.output = output ?? './bundler-cpu-profile.json';
|
|
20
|
+
}
|
|
21
|
+
apply(compiler) {
|
|
22
|
+
const session = new node_inspector_1.default.Session();
|
|
23
|
+
session.connect();
|
|
24
|
+
session.post('Profiler.enable');
|
|
25
|
+
session.post('Profiler.start');
|
|
26
|
+
// In dev/watch mode, we restart the profiler before each compilation
|
|
27
|
+
compiler.hooks.watchRun.tapPromise(BundlerCPUProfilerPlugin.name, async () => {
|
|
28
|
+
session.post('Profiler.start');
|
|
29
|
+
});
|
|
30
|
+
compiler.hooks.done.tapPromise(BundlerCPUProfilerPlugin.name, async () => {
|
|
31
|
+
session.post('Profiler.stop', (error, param) => {
|
|
32
|
+
if (error) {
|
|
33
|
+
console.error('Failed to generate JS CPU profile:', error);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
fs_extra_1.default.writeFile(this.output, JSON.stringify(param.profile)).catch(console.error);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
exports.BundlerCPUProfilerPlugin = BundlerCPUProfilerPlugin;
|