@docusaurus/core 4.0.0-canary-6842 → 4.0.0-canary-6843
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/server/configValidation.js +0 -3
- package/lib/ssg/ssgExecutor.js +8 -30
- package/lib/ssg/ssgRenderer.d.ts +0 -1
- package/lib/ssg/ssgRenderer.js +17 -33
- package/lib/ssg/ssgWorkerThread.js +6 -3
- package/package.json +13 -14
- package/lib/ssg/ssgNodeRequire.d.ts +0 -11
- package/lib/ssg/ssgNodeRequire.js +0 -40
- package/lib/ssg/ssgWorkerInline.d.ts +0 -12
- package/lib/ssg/ssgWorkerInline.js +0 -17
|
@@ -53,7 +53,6 @@ exports.DEFAULT_FASTER_CONFIG = {
|
|
|
53
53
|
mdxCrossCompilerCache: false,
|
|
54
54
|
rspackBundler: false,
|
|
55
55
|
rspackPersistentCache: false,
|
|
56
|
-
ssgWorkerThreads: false,
|
|
57
56
|
gitEagerVcs: false,
|
|
58
57
|
};
|
|
59
58
|
// When using the "faster: true" shortcut
|
|
@@ -65,7 +64,6 @@ exports.DEFAULT_FASTER_CONFIG_TRUE = {
|
|
|
65
64
|
mdxCrossCompilerCache: true,
|
|
66
65
|
rspackBundler: true,
|
|
67
66
|
rspackPersistentCache: true,
|
|
68
|
-
ssgWorkerThreads: true,
|
|
69
67
|
gitEagerVcs: true,
|
|
70
68
|
};
|
|
71
69
|
exports.DEFAULT_FUTURE_V4_CONFIG = {
|
|
@@ -217,7 +215,6 @@ const FASTER_CONFIG_SCHEMA = utils_validation_1.Joi.alternatives()
|
|
|
217
215
|
mdxCrossCompilerCache: utils_validation_1.Joi.boolean(),
|
|
218
216
|
rspackBundler: utils_validation_1.Joi.boolean(),
|
|
219
217
|
rspackPersistentCache: utils_validation_1.Joi.boolean(),
|
|
220
|
-
ssgWorkerThreads: utils_validation_1.Joi.boolean(),
|
|
221
218
|
gitEagerVcs: utils_validation_1.Joi.boolean(),
|
|
222
219
|
}), utils_validation_1.Joi.boolean()
|
|
223
220
|
.required()
|
package/lib/ssg/ssgExecutor.js
CHANGED
|
@@ -18,23 +18,6 @@ const ssgTemplate_1 = require("./ssgTemplate");
|
|
|
18
18
|
const ssgEnv_1 = require("./ssgEnv");
|
|
19
19
|
const ssgUtils_1 = require("./ssgUtils");
|
|
20
20
|
const ssgGlobalResult_1 = require("./ssgGlobalResult");
|
|
21
|
-
const ssgWorkerInline_1 = require("./ssgWorkerInline");
|
|
22
|
-
const createSimpleSSGExecutor = async ({ params, pathnames, }) => {
|
|
23
|
-
return {
|
|
24
|
-
run: () => {
|
|
25
|
-
return logger_1.PerfLogger.async('SSG (current thread)', async () => {
|
|
26
|
-
const ssgResults = await (0, ssgWorkerInline_1.executeSSGInlineTask)({
|
|
27
|
-
pathnames,
|
|
28
|
-
params,
|
|
29
|
-
});
|
|
30
|
-
return (0, ssgGlobalResult_1.createGlobalSSGResult)(ssgResults);
|
|
31
|
-
});
|
|
32
|
-
},
|
|
33
|
-
destroy: async () => {
|
|
34
|
-
// nothing to do
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
21
|
// Sensible default that gives decent performances
|
|
39
22
|
// It's hard to have a perfect formula that works for all hosts
|
|
40
23
|
// Each thread has some creation overhead
|
|
@@ -75,15 +58,10 @@ function getWorkerColorEnv() {
|
|
|
75
58
|
}
|
|
76
59
|
return {};
|
|
77
60
|
}
|
|
78
|
-
|
|
61
|
+
// SSG always runs in worker threads, even for small sites
|
|
62
|
+
// This isolates the server bundle execution from the main Docusaurus process
|
|
63
|
+
async function createSSGExecutor({ params, pathnames, }) {
|
|
79
64
|
const numberOfThreads = getNumberOfThreads(pathnames);
|
|
80
|
-
// When the inferred or provided number of threads is just 1
|
|
81
|
-
// It's not worth it to use a thread pool
|
|
82
|
-
// This also allows users to disable the thread pool with the env variable
|
|
83
|
-
// DOCUSAURUS_SSG_WORKER_THREADS=1
|
|
84
|
-
if (numberOfThreads === 1) {
|
|
85
|
-
return createSimpleSSGExecutor({ params, pathnames });
|
|
86
|
-
}
|
|
87
65
|
const pool = await logger_1.PerfLogger.async(`Create SSG thread pool - ${logger_1.default.cyan(numberOfThreads)} threads`, async () => {
|
|
88
66
|
const Tinypool = await import('tinypool').then((m) => m.default);
|
|
89
67
|
const workerURL = (0, node_url_1.pathToFileURL)(path.resolve(__dirname, 'ssgWorkerThread.js'));
|
|
@@ -138,7 +116,7 @@ const createPooledSSGExecutor = async ({ params, pathnames, }) => {
|
|
|
138
116
|
await pool.destroy();
|
|
139
117
|
},
|
|
140
118
|
};
|
|
141
|
-
}
|
|
119
|
+
}
|
|
142
120
|
async function executeSSG({ props, serverBundlePath, clientManifestPath, router, }) {
|
|
143
121
|
const params = await (0, ssgParams_1.createSSGParams)({
|
|
144
122
|
serverBundlePath,
|
|
@@ -153,10 +131,10 @@ async function executeSSG({ props, serverBundlePath, clientManifestPath, router,
|
|
|
153
131
|
logger_1.PerfLogger.end('Generate Hash Router entry point');
|
|
154
132
|
return { collectedData: {} };
|
|
155
133
|
}
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
:
|
|
159
|
-
|
|
134
|
+
const executor = await createSSGExecutor({
|
|
135
|
+
params,
|
|
136
|
+
pathnames: props.routesPaths,
|
|
137
|
+
});
|
|
160
138
|
const result = await executor.run();
|
|
161
139
|
await executor.destroy();
|
|
162
140
|
return result;
|
package/lib/ssg/ssgRenderer.d.ts
CHANGED
|
@@ -21,7 +21,6 @@ export type SSGError = {
|
|
|
21
21
|
};
|
|
22
22
|
export type SSGResult = SSGSuccess | SSGError;
|
|
23
23
|
export type SSGRenderer = {
|
|
24
|
-
shutdown: () => Promise<void>;
|
|
25
24
|
renderPathnames: (pathnames: string[]) => Promise<SSGResult[]>;
|
|
26
25
|
};
|
|
27
26
|
export declare function loadSSGRenderer({ params, }: {
|
package/lib/ssg/ssgRenderer.js
CHANGED
|
@@ -8,46 +8,33 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.loadSSGRenderer = loadSSGRenderer;
|
|
10
10
|
const tslib_1 = require("tslib");
|
|
11
|
-
const
|
|
11
|
+
const module_1 = require("module");
|
|
12
12
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const eval_1 = tslib_1.__importDefault(require("eval"));
|
|
13
|
+
const vm_1 = require("vm");
|
|
14
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
16
15
|
const p_map_1 = tslib_1.__importDefault(require("p-map"));
|
|
17
16
|
const logger_1 = tslib_1.__importStar(require("@docusaurus/logger"));
|
|
18
17
|
const bundler_1 = require("@docusaurus/bundler");
|
|
19
18
|
const ssgTemplate_1 = require("./ssgTemplate");
|
|
20
19
|
const ssgEnv_1 = require("./ssgEnv");
|
|
21
20
|
const ssgUtils_1 = require("./ssgUtils");
|
|
22
|
-
|
|
21
|
+
// Evaluates the server bundle as a CommonJS module, like Node.js would do
|
|
22
|
+
// We don't use an isolated vm context on purpose: SSG runs in worker threads
|
|
23
|
+
// that already isolate it from the main Docusaurus process
|
|
24
|
+
// Running in the current context is faster and avoids reading all the globals
|
|
25
|
+
function evaluateServerBundle({ source, serverBundlePath, }) {
|
|
26
|
+
const moduleFunction = (0, vm_1.compileFunction)(source, ['exports', 'require', 'module', '__filename', '__dirname'], { filename: serverBundlePath });
|
|
27
|
+
const module = { exports: {} };
|
|
28
|
+
moduleFunction.call(module.exports, module.exports, (0, module_1.createRequire)(serverBundlePath), module, serverBundlePath, path_1.default.dirname(serverBundlePath));
|
|
29
|
+
return module.exports;
|
|
30
|
+
}
|
|
23
31
|
async function loadAppRenderer({ serverBundlePath, }) {
|
|
24
|
-
const source = await logger_1.PerfLogger.async(`Load server bundle`, () => fs_extra_1.default.readFile(serverBundlePath));
|
|
25
|
-
const
|
|
26
|
-
const ssgRequire = (0, ssgNodeRequire_1.createSSGRequire)(serverBundlePath);
|
|
27
|
-
const globals = {
|
|
28
|
-
// When using "new URL('file.js', import.meta.url)", Webpack will emit
|
|
29
|
-
// __filename, and this plugin will throw. not sure the __filename value
|
|
30
|
-
// has any importance for this plugin, just using an empty string to
|
|
31
|
-
// avoid the error. See https://github.com/facebook/docusaurus/issues/4922
|
|
32
|
-
__filename: '',
|
|
33
|
-
// This uses module.createRequire() instead of very old "require-like" lib
|
|
34
|
-
// See also: https://github.com/pierrec/node-eval/issues/33
|
|
35
|
-
require: ssgRequire.require,
|
|
36
|
-
};
|
|
37
|
-
const serverEntry = await logger_1.PerfLogger.async(`Evaluate server bundle`, () => (0, eval_1.default)(source,
|
|
38
|
-
/* filename: */ filename,
|
|
39
|
-
/* scope: */ globals,
|
|
40
|
-
/* includeGlobals: */ true));
|
|
32
|
+
const source = await logger_1.PerfLogger.async(`Load server bundle`, () => fs_extra_1.default.readFile(serverBundlePath, 'utf8'));
|
|
33
|
+
const serverEntry = await logger_1.PerfLogger.async(`Evaluate server bundle`, () => evaluateServerBundle({ source, serverBundlePath }));
|
|
41
34
|
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
|
|
42
|
-
throw new Error(`Docusaurus Bug: server bundle export from "${
|
|
35
|
+
throw new Error(`Docusaurus Bug: server bundle export from "${serverBundlePath}" must be a function that renders the Docusaurus React app, not ${typeof serverEntry?.default}`);
|
|
43
36
|
}
|
|
44
|
-
|
|
45
|
-
ssgRequire.cleanup();
|
|
46
|
-
}
|
|
47
|
-
return {
|
|
48
|
-
render: serverEntry.default,
|
|
49
|
-
shutdown,
|
|
50
|
-
};
|
|
37
|
+
return { render: serverEntry.default };
|
|
51
38
|
}
|
|
52
39
|
async function loadSSGRenderer({ params, }) {
|
|
53
40
|
const [appRenderer, htmlMinifier, ssgTemplate] = await Promise.all([
|
|
@@ -69,9 +56,6 @@ async function loadSSGRenderer({ params, }) {
|
|
|
69
56
|
ssgTemplate,
|
|
70
57
|
}), { concurrency: ssgEnv_1.SSGConcurrency });
|
|
71
58
|
},
|
|
72
|
-
shutdown: async () => {
|
|
73
|
-
await appRenderer.shutdown();
|
|
74
|
-
},
|
|
75
59
|
};
|
|
76
60
|
}
|
|
77
61
|
// We reduce the page collected data structure after the HTML file is written
|
|
@@ -20,6 +20,12 @@ if (!params) {
|
|
|
20
20
|
throw new Error(`SSG Worker Thread workerData params missing`);
|
|
21
21
|
}
|
|
22
22
|
const WorkerLogPrefix = `SSG Worker ${logger_1.default.name(workerId)}`;
|
|
23
|
+
// The server bundle runs in the worker thread global context
|
|
24
|
+
// Web Storage should not be available during SSG, like it was before Node 25
|
|
25
|
+
// Reading it emits a warning on Node 25+ unless --localstorage-file is used
|
|
26
|
+
// Deleting it only affects this worker thread, not the main thread
|
|
27
|
+
Reflect.deleteProperty(globalThis, 'localStorage');
|
|
28
|
+
Reflect.deleteProperty(globalThis, 'sessionStorage');
|
|
23
29
|
// We only load once the SSG rendered (expensive), NOT once per worker task
|
|
24
30
|
// TODO check potential memory leak?
|
|
25
31
|
const appRendererPromise = logger_1.PerfLogger.async(`${WorkerLogPrefix} - Initialization`, () => (0, ssgRenderer_js_1.loadSSGRenderer)({
|
|
@@ -28,8 +34,5 @@ const appRendererPromise = logger_1.PerfLogger.async(`${WorkerLogPrefix} - Initi
|
|
|
28
34
|
async function executeSSGWorkerThreadTask(task) {
|
|
29
35
|
const appRenderer = await appRendererPromise;
|
|
30
36
|
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));
|
|
31
|
-
// Afaik it's not needed to shutdown here,
|
|
32
|
-
// The thread pool destroys worker thread and releases worker thread memory
|
|
33
|
-
// await appRenderer.shutdown();
|
|
34
37
|
return ssgResults;
|
|
35
38
|
}
|
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": "4.0.0-canary-
|
|
4
|
+
"version": "4.0.0-canary-6843",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -33,14 +33,14 @@
|
|
|
33
33
|
"url": "https://github.com/facebook/docusaurus/issues"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@docusaurus/babel": "4.0.0-canary-
|
|
37
|
-
"@docusaurus/bundler": "4.0.0-canary-
|
|
38
|
-
"@docusaurus/glob": "4.0.0-canary-
|
|
39
|
-
"@docusaurus/logger": "4.0.0-canary-
|
|
40
|
-
"@docusaurus/mdx-loader": "4.0.0-canary-
|
|
41
|
-
"@docusaurus/utils": "4.0.0-canary-
|
|
42
|
-
"@docusaurus/utils-common": "4.0.0-canary-
|
|
43
|
-
"@docusaurus/utils-validation": "4.0.0-canary-
|
|
36
|
+
"@docusaurus/babel": "4.0.0-canary-6843",
|
|
37
|
+
"@docusaurus/bundler": "4.0.0-canary-6843",
|
|
38
|
+
"@docusaurus/glob": "4.0.0-canary-6843",
|
|
39
|
+
"@docusaurus/logger": "4.0.0-canary-6843",
|
|
40
|
+
"@docusaurus/mdx-loader": "4.0.0-canary-6843",
|
|
41
|
+
"@docusaurus/utils": "4.0.0-canary-6843",
|
|
42
|
+
"@docusaurus/utils-common": "4.0.0-canary-6843",
|
|
43
|
+
"@docusaurus/utils-validation": "4.0.0-canary-6843",
|
|
44
44
|
"boxen": "^8.0.1",
|
|
45
45
|
"cli-table3": "^0.6.5",
|
|
46
46
|
"combine-promises": "^1.2.0",
|
|
@@ -49,7 +49,6 @@
|
|
|
49
49
|
"detect-port": "^2.1.0",
|
|
50
50
|
"escape-html": "^1.0.3",
|
|
51
51
|
"eta": "^4.6.0",
|
|
52
|
-
"eval": "^0.1.8",
|
|
53
52
|
"execa": "^10.0.1",
|
|
54
53
|
"fs-extra": "^11.4.0",
|
|
55
54
|
"html-tags": "^5.1.0",
|
|
@@ -76,8 +75,8 @@
|
|
|
76
75
|
"webpack-merge": "^6.0.1"
|
|
77
76
|
},
|
|
78
77
|
"devDependencies": {
|
|
79
|
-
"@docusaurus/module-type-aliases": "4.0.0-canary-
|
|
80
|
-
"@docusaurus/types": "4.0.0-canary-
|
|
78
|
+
"@docusaurus/module-type-aliases": "4.0.0-canary-6843",
|
|
79
|
+
"@docusaurus/types": "4.0.0-canary-6843",
|
|
81
80
|
"@total-typescript/shoehorn": "^0.1.2",
|
|
82
81
|
"@types/escape-html": "^1.0.4",
|
|
83
82
|
"@types/history": "^4.7.11",
|
|
@@ -92,7 +91,7 @@
|
|
|
92
91
|
"utility-types": "^3.11.0"
|
|
93
92
|
},
|
|
94
93
|
"peerDependencies": {
|
|
95
|
-
"@docusaurus/faster": "4.0.0-canary-
|
|
94
|
+
"@docusaurus/faster": "4.0.0-canary-6843",
|
|
96
95
|
"@mdx-js/react": "^3.1.1",
|
|
97
96
|
"react": "^19.3.0",
|
|
98
97
|
"react-dom": "^19.3.0"
|
|
@@ -105,5 +104,5 @@
|
|
|
105
104
|
"engines": {
|
|
106
105
|
"node": ">=24.21"
|
|
107
106
|
},
|
|
108
|
-
"gitHead": "
|
|
107
|
+
"gitHead": "a81a4292fc3069231904bcd6dad29ffea849e3de"
|
|
109
108
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
export type SSGNodeRequire = {
|
|
8
|
-
require: NodeJS.Require;
|
|
9
|
-
cleanup: () => void;
|
|
10
|
-
};
|
|
11
|
-
export declare function createSSGRequire(serverBundlePath: string): SSGNodeRequire;
|
|
@@ -1,40 +0,0 @@
|
|
|
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.createSSGRequire = createSSGRequire;
|
|
10
|
-
const module_1 = require("module");
|
|
11
|
-
// The eval/vm.Script used for running the server bundle need a require() impl
|
|
12
|
-
// This impl has to be relative to the server bundler path
|
|
13
|
-
// This enables the server bundle to resolve relative paths such as:
|
|
14
|
-
// - require('./assets/js/some-chunk.123456.js')
|
|
15
|
-
//
|
|
16
|
-
// Unfortunately, Node.js vm.Script doesn't isolate memory / require.cache
|
|
17
|
-
// This means that if we build multiple Docusaurus localized sites in a row
|
|
18
|
-
// The Node.js require cache will keep growing and retain in memory the JS
|
|
19
|
-
// assets of the former SSG builds
|
|
20
|
-
// We have to clean up the node require cache manually to avoid leaking memory!
|
|
21
|
-
// See also https://x.com/sebastienlorber/status/1848399310116831702
|
|
22
|
-
function createSSGRequire(serverBundlePath) {
|
|
23
|
-
const realRequire = (0, module_1.createRequire)(serverBundlePath);
|
|
24
|
-
const allRequiredIds = [];
|
|
25
|
-
const ssgRequireFunction = (id) => {
|
|
26
|
-
const module = realRequire(id);
|
|
27
|
-
allRequiredIds.push(id);
|
|
28
|
-
return module;
|
|
29
|
-
};
|
|
30
|
-
const cleanup = () => {
|
|
31
|
-
allRequiredIds.forEach((id) => {
|
|
32
|
-
delete realRequire.cache[realRequire.resolve(id)];
|
|
33
|
-
});
|
|
34
|
-
};
|
|
35
|
-
ssgRequireFunction.resolve = realRequire.resolve;
|
|
36
|
-
ssgRequireFunction.cache = realRequire.cache;
|
|
37
|
-
ssgRequireFunction.extensions = realRequire.extensions;
|
|
38
|
-
ssgRequireFunction.main = realRequire.main;
|
|
39
|
-
return { require: ssgRequireFunction, cleanup };
|
|
40
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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[]>;
|
|
@@ -1,17 +0,0 @@
|
|
|
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
|
-
}
|