@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
package/lib/ssg.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
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.generateStaticFiles = exports.loadAppRenderer = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
12
|
+
const module_1 = require("module");
|
|
13
|
+
const path_1 = tslib_1.__importDefault(require("path"));
|
|
14
|
+
const lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
15
|
+
const eval_1 = tslib_1.__importDefault(require("eval"));
|
|
16
|
+
const p_map_1 = tslib_1.__importDefault(require("p-map"));
|
|
17
|
+
const html_minifier_terser_1 = require("html-minifier-terser");
|
|
18
|
+
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
19
|
+
const utils_1 = require("./utils");
|
|
20
|
+
const templates_1 = require("./templates/templates");
|
|
21
|
+
// Secret way to set SSR plugin concurrency option
|
|
22
|
+
// Waiting for feedback before documenting this officially?
|
|
23
|
+
const Concurrency = process.env.DOCUSAURUS_SSR_CONCURRENCY
|
|
24
|
+
? parseInt(process.env.DOCUSAURUS_SSR_CONCURRENCY, 10)
|
|
25
|
+
: // Not easy to define a reasonable option default
|
|
26
|
+
// Will still be better than Infinity
|
|
27
|
+
// See also https://github.com/sindresorhus/p-map/issues/24
|
|
28
|
+
32;
|
|
29
|
+
async function loadAppRenderer({ serverBundlePath, }) {
|
|
30
|
+
const source = await utils_1.PerfLogger.async(`Load server bundle`, () => fs_extra_1.default.readFile(serverBundlePath));
|
|
31
|
+
utils_1.PerfLogger.log(`Server bundle size = ${(source.length / 1024000).toFixed(3)} MB`);
|
|
32
|
+
const filename = path_1.default.basename(serverBundlePath);
|
|
33
|
+
const globals = {
|
|
34
|
+
// When using "new URL('file.js', import.meta.url)", Webpack will emit
|
|
35
|
+
// __filename, and this plugin will throw. not sure the __filename value
|
|
36
|
+
// has any importance for this plugin, just using an empty string to
|
|
37
|
+
// avoid the error. See https://github.com/facebook/docusaurus/issues/4922
|
|
38
|
+
__filename: '',
|
|
39
|
+
// This uses module.createRequire() instead of very old "require-like" lib
|
|
40
|
+
// See also: https://github.com/pierrec/node-eval/issues/33
|
|
41
|
+
require: (0, module_1.createRequire)(serverBundlePath),
|
|
42
|
+
};
|
|
43
|
+
const serverEntry = await utils_1.PerfLogger.async(`Evaluate server bundle`, () => (0, eval_1.default)(source,
|
|
44
|
+
/* filename: */ filename,
|
|
45
|
+
/* scope: */ globals,
|
|
46
|
+
/* includeGlobals: */ true));
|
|
47
|
+
if (!serverEntry?.default || typeof serverEntry.default !== 'function') {
|
|
48
|
+
throw new Error(`Server bundle export from "${filename}" must be a function that renders the Docusaurus React app.`);
|
|
49
|
+
}
|
|
50
|
+
return serverEntry.default;
|
|
51
|
+
}
|
|
52
|
+
exports.loadAppRenderer = loadAppRenderer;
|
|
53
|
+
function pathnameToFilename({ pathname, trailingSlash, }) {
|
|
54
|
+
const outputFileName = pathname.replace(/^[/\\]/, ''); // Remove leading slashes for webpack-dev-server
|
|
55
|
+
// Paths ending with .html are left untouched
|
|
56
|
+
if (/\.html?$/i.test(outputFileName)) {
|
|
57
|
+
return outputFileName;
|
|
58
|
+
}
|
|
59
|
+
// Legacy retro-compatible behavior
|
|
60
|
+
if (typeof trailingSlash === 'undefined') {
|
|
61
|
+
return path_1.default.join(outputFileName, 'index.html');
|
|
62
|
+
}
|
|
63
|
+
// New behavior: we can say if we prefer file/folder output
|
|
64
|
+
// Useful resource: https://github.com/slorber/trailing-slash-guide
|
|
65
|
+
if (pathname === '' || pathname.endsWith('/') || trailingSlash) {
|
|
66
|
+
return path_1.default.join(outputFileName, 'index.html');
|
|
67
|
+
}
|
|
68
|
+
return `${outputFileName}.html`;
|
|
69
|
+
}
|
|
70
|
+
async function generateStaticFiles({ pathnames, renderer, params, }) {
|
|
71
|
+
// Note that we catch all async errors on purpose
|
|
72
|
+
// Docusaurus presents all the SSG errors to the user, not just the first one
|
|
73
|
+
const results = await (0, p_map_1.default)(pathnames, async (pathname) => generateStaticFile({
|
|
74
|
+
pathname,
|
|
75
|
+
renderer,
|
|
76
|
+
params,
|
|
77
|
+
}).then((result) => ({ pathname, result, error: null }), (error) => ({ pathname, result: null, error: error })), { concurrency: Concurrency });
|
|
78
|
+
const [allSSGErrors, allSSGSuccesses] = lodash_1.default.partition(results, (r) => !!r.error);
|
|
79
|
+
if (allSSGErrors.length > 0) {
|
|
80
|
+
const message = `Docusaurus static site generation failed for ${allSSGErrors.length} path${allSSGErrors.length ? 's' : ''}:\n- ${allSSGErrors
|
|
81
|
+
.map((ssgError) => logger_1.default.path(ssgError.pathname))
|
|
82
|
+
.join('\n- ')}`;
|
|
83
|
+
// Note logging this error properly require using inspect(error,{depth})
|
|
84
|
+
// See https://github.com/nodejs/node/issues/51637
|
|
85
|
+
throw new Error(message, {
|
|
86
|
+
cause: new AggregateError(allSSGErrors.map((ssgError) => ssgError.error)),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
const collectedData = lodash_1.default.chain(allSSGSuccesses)
|
|
90
|
+
.keyBy((success) => success.pathname)
|
|
91
|
+
.mapValues((ssgSuccess) => ssgSuccess.result.collectedData)
|
|
92
|
+
.value();
|
|
93
|
+
return { collectedData };
|
|
94
|
+
}
|
|
95
|
+
exports.generateStaticFiles = generateStaticFiles;
|
|
96
|
+
async function generateStaticFile({ pathname, renderer, params, }) {
|
|
97
|
+
try {
|
|
98
|
+
// This only renders the app HTML
|
|
99
|
+
const result = await renderer({
|
|
100
|
+
pathname,
|
|
101
|
+
});
|
|
102
|
+
// This renders the full page HTML, including head tags...
|
|
103
|
+
const fullPageHtml = (0, templates_1.renderSSRTemplate)({
|
|
104
|
+
params,
|
|
105
|
+
result,
|
|
106
|
+
});
|
|
107
|
+
const content = await minifyHtml(fullPageHtml);
|
|
108
|
+
await writeStaticFile({
|
|
109
|
+
pathname,
|
|
110
|
+
content,
|
|
111
|
+
params,
|
|
112
|
+
});
|
|
113
|
+
return result;
|
|
114
|
+
}
|
|
115
|
+
catch (errorUnknown) {
|
|
116
|
+
const error = errorUnknown;
|
|
117
|
+
const tips = getSSGErrorTips(error);
|
|
118
|
+
const message = logger_1.default.interpolate `Can't render static file for pathname path=${pathname}${tips ? `\n\n${tips}` : ''}`;
|
|
119
|
+
throw new Error(message, {
|
|
120
|
+
cause: error,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function getSSGErrorTips(error) {
|
|
125
|
+
const parts = [];
|
|
126
|
+
const isNotDefinedErrorRegex = /(?:window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i;
|
|
127
|
+
if (isNotDefinedErrorRegex.test(error.message)) {
|
|
128
|
+
parts.push(`It looks like you are using code that should run on the client-side only.
|
|
129
|
+
To get around it, try using one of:
|
|
130
|
+
- ${logger_1.default.code('<BrowserOnly>')} (${logger_1.default.url('https://docusaurus.io/docs/docusaurus-core/#browseronly')})
|
|
131
|
+
- ${logger_1.default.code('ExecutionEnvironment')} (${logger_1.default.url('https://docusaurus.io/docs/docusaurus-core/#executionenvironment')}).
|
|
132
|
+
It might also require to wrap your client code in ${logger_1.default.code('useEffect')} hook and/or import a third-party library dynamically (if any).`);
|
|
133
|
+
}
|
|
134
|
+
return parts.join('\n');
|
|
135
|
+
}
|
|
136
|
+
async function writeStaticFile({ content, pathname, params, }) {
|
|
137
|
+
function removeBaseUrl(p, baseUrl) {
|
|
138
|
+
return baseUrl === '/' ? p : p.replace(new RegExp(`^${baseUrl}`), '/');
|
|
139
|
+
}
|
|
140
|
+
const filename = pathnameToFilename({
|
|
141
|
+
pathname: removeBaseUrl(pathname, params.baseUrl),
|
|
142
|
+
trailingSlash: params.trailingSlash,
|
|
143
|
+
});
|
|
144
|
+
const filePath = path_1.default.join(params.outDir, filename);
|
|
145
|
+
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
146
|
+
await fs_extra_1.default.writeFile(filePath, content);
|
|
147
|
+
}
|
|
148
|
+
async function minifyHtml(html) {
|
|
149
|
+
try {
|
|
150
|
+
if (process.env.SKIP_HTML_MINIFICATION === 'true') {
|
|
151
|
+
return html;
|
|
152
|
+
}
|
|
153
|
+
// Minify html with https://github.com/DanielRuf/html-minifier-terser
|
|
154
|
+
return await (0, html_minifier_terser_1.minify)(html, {
|
|
155
|
+
removeComments: false,
|
|
156
|
+
removeRedundantAttributes: true,
|
|
157
|
+
removeEmptyAttributes: true,
|
|
158
|
+
removeScriptTypeAttributes: true,
|
|
159
|
+
removeStyleLinkTypeAttributes: true,
|
|
160
|
+
useShortDoctype: true,
|
|
161
|
+
minifyJS: true,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
throw new Error('HTML minification failed', { cause: err });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
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 '../ssg';
|
|
8
|
+
import type { AppRenderResult } from '../common';
|
|
9
|
+
export type SSRTemplateData = {
|
|
10
|
+
appHtml: string;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
htmlAttributes: string;
|
|
13
|
+
bodyAttributes: string;
|
|
14
|
+
headTags: string;
|
|
15
|
+
preBodyTags: string;
|
|
16
|
+
postBodyTags: string;
|
|
17
|
+
metaAttributes: string[];
|
|
18
|
+
scripts: string[];
|
|
19
|
+
stylesheets: string[];
|
|
20
|
+
noIndex: boolean;
|
|
21
|
+
version: string;
|
|
22
|
+
};
|
|
23
|
+
export type SSRTemplateCompiled = (data: SSRTemplateData) => string;
|
|
24
|
+
export declare function compileSSRTemplate(template: string): Promise<SSRTemplateCompiled>;
|
|
25
|
+
export declare function renderSSRTemplate({ params, result, }: {
|
|
26
|
+
params: SSGParams;
|
|
27
|
+
result: AppRenderResult;
|
|
28
|
+
}): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
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.renderSSRTemplate = exports.compileSSRTemplate = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const eta = tslib_1.__importStar(require("eta"));
|
|
12
|
+
const react_loadable_ssr_addon_v5_slorber_1 = require("react-loadable-ssr-addon-v5-slorber");
|
|
13
|
+
async function compileSSRTemplate(template) {
|
|
14
|
+
const compiledTemplate = eta.compile(template.trim(), {
|
|
15
|
+
rmWhitespace: true,
|
|
16
|
+
});
|
|
17
|
+
return (data) => compiledTemplate(data, eta.defaultConfig);
|
|
18
|
+
}
|
|
19
|
+
exports.compileSSRTemplate = compileSSRTemplate;
|
|
20
|
+
/**
|
|
21
|
+
* Given a list of modules that were SSR an d
|
|
22
|
+
* @param modules
|
|
23
|
+
* @param manifest
|
|
24
|
+
*/
|
|
25
|
+
function getScriptsAndStylesheets({ modules, manifest, }) {
|
|
26
|
+
// Get all required assets for this particular page
|
|
27
|
+
// based on client manifest information.
|
|
28
|
+
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
|
|
29
|
+
const bundles = (0, react_loadable_ssr_addon_v5_slorber_1.getBundles)(manifest, modulesToBeLoaded);
|
|
30
|
+
const stylesheets = (bundles.css ?? []).map((b) => b.file);
|
|
31
|
+
const scripts = (bundles.js ?? []).map((b) => b.file);
|
|
32
|
+
return { scripts, stylesheets };
|
|
33
|
+
}
|
|
34
|
+
function renderSSRTemplate({ params, result, }) {
|
|
35
|
+
const { baseUrl, headTags, preBodyTags, postBodyTags, manifest, noIndex, DOCUSAURUS_VERSION, ssrTemplate, } = params;
|
|
36
|
+
const { html: appHtml, collectedData: { modules, helmet }, } = result;
|
|
37
|
+
const { scripts, stylesheets } = getScriptsAndStylesheets({ manifest, modules });
|
|
38
|
+
const htmlAttributes = helmet.htmlAttributes.toString();
|
|
39
|
+
const bodyAttributes = helmet.bodyAttributes.toString();
|
|
40
|
+
const metaStrings = [
|
|
41
|
+
helmet.title.toString(),
|
|
42
|
+
helmet.meta.toString(),
|
|
43
|
+
helmet.link.toString(),
|
|
44
|
+
helmet.script.toString(),
|
|
45
|
+
];
|
|
46
|
+
const metaAttributes = metaStrings.filter(Boolean);
|
|
47
|
+
const data = {
|
|
48
|
+
appHtml,
|
|
49
|
+
baseUrl,
|
|
50
|
+
htmlAttributes,
|
|
51
|
+
bodyAttributes,
|
|
52
|
+
headTags,
|
|
53
|
+
preBodyTags,
|
|
54
|
+
postBodyTags,
|
|
55
|
+
metaAttributes,
|
|
56
|
+
scripts,
|
|
57
|
+
stylesheets,
|
|
58
|
+
noIndex,
|
|
59
|
+
version: DOCUSAURUS_VERSION,
|
|
60
|
+
};
|
|
61
|
+
return ssrTemplate(data);
|
|
62
|
+
}
|
|
63
|
+
exports.renderSSRTemplate = renderSSRTemplate;
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const PerfDebuggingEnabled: boolean;
|
|
2
|
+
type PerfLoggerAPI = {
|
|
3
|
+
start: (label: string) => void;
|
|
4
|
+
end: (label: string) => void;
|
|
5
|
+
log: (message: string) => void;
|
|
6
|
+
async: <Result>(label: string, asyncFn: () => Result | Promise<Result>) => Promise<Result>;
|
|
7
|
+
};
|
|
8
|
+
export declare const PerfLogger: PerfLoggerAPI;
|
|
9
|
+
export {};
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PerfLogger = exports.PerfDebuggingEnabled = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
/**
|
|
6
|
+
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
7
|
+
*
|
|
8
|
+
* This source code is licensed under the MIT license found in the
|
|
9
|
+
* LICENSE file in the root directory of this source tree.
|
|
10
|
+
*/
|
|
11
|
+
const async_hooks_1 = require("async_hooks");
|
|
12
|
+
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
13
|
+
// For now this is a private env variable we use internally
|
|
14
|
+
// But we'll want to expose this feature officially some day
|
|
15
|
+
exports.PerfDebuggingEnabled = !!process.env.DOCUSAURUS_PERF_LOGGER;
|
|
16
|
+
const Thresholds = {
|
|
17
|
+
min: 5,
|
|
18
|
+
yellow: 100,
|
|
19
|
+
red: 1000,
|
|
20
|
+
};
|
|
21
|
+
const PerfPrefix = logger_1.default.yellow(`[PERF] `);
|
|
22
|
+
// This is what enables to "see the parent stack" for each log
|
|
23
|
+
// Parent1 > Parent2 > Parent3 > child trace
|
|
24
|
+
const ParentPrefix = new async_hooks_1.AsyncLocalStorage();
|
|
25
|
+
function applyParentPrefix(label) {
|
|
26
|
+
const parentPrefix = ParentPrefix.getStore();
|
|
27
|
+
return parentPrefix ? `${parentPrefix} > ${label}` : label;
|
|
28
|
+
}
|
|
29
|
+
function createPerfLogger() {
|
|
30
|
+
if (!exports.PerfDebuggingEnabled) {
|
|
31
|
+
const noop = () => { };
|
|
32
|
+
return {
|
|
33
|
+
start: noop,
|
|
34
|
+
end: noop,
|
|
35
|
+
log: noop,
|
|
36
|
+
async: async (_label, asyncFn) => asyncFn(),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const formatDuration = (duration) => {
|
|
40
|
+
if (duration > Thresholds.red) {
|
|
41
|
+
return logger_1.default.red(`${(duration / 1000).toFixed(2)} seconds!`);
|
|
42
|
+
}
|
|
43
|
+
else if (duration > Thresholds.yellow) {
|
|
44
|
+
return logger_1.default.yellow(`${duration.toFixed(2)} ms`);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return logger_1.default.green(`${duration.toFixed(2)} ms`);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const logDuration = (label, duration) => {
|
|
51
|
+
if (duration < Thresholds.min) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log(`${PerfPrefix + label} - ${formatDuration(duration)}`);
|
|
55
|
+
};
|
|
56
|
+
const start = (label) => performance.mark(label);
|
|
57
|
+
const end = (label) => {
|
|
58
|
+
const { duration } = performance.measure(label);
|
|
59
|
+
performance.clearMarks(label);
|
|
60
|
+
logDuration(applyParentPrefix(label), duration);
|
|
61
|
+
};
|
|
62
|
+
const log = (label) => console.log(PerfPrefix + applyParentPrefix(label));
|
|
63
|
+
const async = async (label, asyncFn) => {
|
|
64
|
+
const finalLabel = applyParentPrefix(label);
|
|
65
|
+
const before = performance.now();
|
|
66
|
+
const result = await ParentPrefix.run(finalLabel, () => asyncFn());
|
|
67
|
+
const duration = performance.now() - before;
|
|
68
|
+
logDuration(finalLabel, duration);
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
return {
|
|
72
|
+
start,
|
|
73
|
+
end,
|
|
74
|
+
log,
|
|
75
|
+
async,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
exports.PerfLogger = createPerfLogger();
|
package/lib/webpack/base.d.ts
CHANGED
|
@@ -8,4 +8,8 @@ import type { Configuration } from 'webpack';
|
|
|
8
8
|
import type { Props } from '@docusaurus/types';
|
|
9
9
|
export declare const clientDir: string;
|
|
10
10
|
export declare function excludeJS(modulePath: string): boolean;
|
|
11
|
-
export declare function createBaseConfig(props
|
|
11
|
+
export declare function createBaseConfig({ props, isServer, minify, }: {
|
|
12
|
+
props: Props;
|
|
13
|
+
isServer: boolean;
|
|
14
|
+
minify: boolean;
|
|
15
|
+
}): Promise<Configuration>;
|
package/lib/webpack/base.js
CHANGED
|
@@ -13,6 +13,7 @@ const path_1 = tslib_1.__importDefault(require("path"));
|
|
|
13
13
|
const mini_css_extract_plugin_1 = tslib_1.__importDefault(require("mini-css-extract-plugin"));
|
|
14
14
|
const utils_1 = require("@docusaurus/utils");
|
|
15
15
|
const utils_2 = require("./utils");
|
|
16
|
+
const minification_1 = require("./minification");
|
|
16
17
|
const aliases_1 = require("./aliases");
|
|
17
18
|
const CSS_REGEX = /\.css$/i;
|
|
18
19
|
const CSS_MODULE_REGEX = /\.module\.css$/i;
|
|
@@ -32,12 +33,11 @@ function excludeJS(modulePath) {
|
|
|
32
33
|
!LibrariesToTranspileRegex.test(modulePath));
|
|
33
34
|
}
|
|
34
35
|
exports.excludeJS = excludeJS;
|
|
35
|
-
async function createBaseConfig(props, isServer, minify
|
|
36
|
+
async function createBaseConfig({ props, isServer, minify, }) {
|
|
36
37
|
const { outDir, siteDir, siteConfig, siteConfigPath, baseUrl, generatedFilesDir, routesPaths, siteMetadata, plugins, } = props;
|
|
37
38
|
const totalPages = routesPaths.length;
|
|
38
39
|
const isProd = process.env.NODE_ENV === 'production';
|
|
39
|
-
const minimizeEnabled = minify && isProd
|
|
40
|
-
const useSimpleCssMinifier = process.env.USE_SIMPLE_CSS_MINIFIER === 'true';
|
|
40
|
+
const minimizeEnabled = minify && isProd;
|
|
41
41
|
const fileLoaderUtils = (0, utils_1.getFileLoaderUtils)();
|
|
42
42
|
const name = isServer ? 'server' : 'client';
|
|
43
43
|
const mode = isProd ? 'production' : 'development';
|
|
@@ -124,9 +124,7 @@ async function createBaseConfig(props, isServer, minify = true) {
|
|
|
124
124
|
// Only minimize client bundle in production because server bundle is only
|
|
125
125
|
// used for static site generation
|
|
126
126
|
minimize: minimizeEnabled,
|
|
127
|
-
minimizer: minimizeEnabled
|
|
128
|
-
? (0, utils_2.getMinimizer)(useSimpleCssMinifier)
|
|
129
|
-
: undefined,
|
|
127
|
+
minimizer: minimizeEnabled ? (0, minification_1.getMinimizer)() : undefined,
|
|
130
128
|
splitChunks: isServer
|
|
131
129
|
? false
|
|
132
130
|
: {
|
package/lib/webpack/client.d.ts
CHANGED
|
@@ -6,4 +6,18 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { Props } from '@docusaurus/types';
|
|
8
8
|
import type { Configuration } from 'webpack';
|
|
9
|
-
export
|
|
9
|
+
export declare function createStartClientConfig({ props, minify, poll, }: {
|
|
10
|
+
props: Props;
|
|
11
|
+
minify: boolean;
|
|
12
|
+
poll: number | boolean | undefined;
|
|
13
|
+
}): Promise<{
|
|
14
|
+
clientConfig: Configuration;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function createBuildClientConfig({ props, minify, bundleAnalyzer, }: {
|
|
17
|
+
props: Props;
|
|
18
|
+
minify: boolean;
|
|
19
|
+
bundleAnalyzer: boolean;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
config: Configuration;
|
|
22
|
+
clientManifestPath: string;
|
|
23
|
+
}>;
|
package/lib/webpack/client.js
CHANGED
|
@@ -6,19 +6,36 @@
|
|
|
6
6
|
* LICENSE file in the root directory of this source tree.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.createBuildClientConfig = exports.createStartClientConfig = void 0;
|
|
9
10
|
const tslib_1 = require("tslib");
|
|
10
11
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
11
12
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
12
13
|
const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
|
|
13
14
|
const webpackbar_1 = tslib_1.__importDefault(require("webpackbar"));
|
|
14
|
-
const webpack_1 = require("webpack");
|
|
15
|
+
const webpack_1 = tslib_1.__importDefault(require("webpack"));
|
|
16
|
+
const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
|
|
17
|
+
const react_loadable_ssr_addon_v5_slorber_1 = tslib_1.__importDefault(require("react-loadable-ssr-addon-v5-slorber"));
|
|
18
|
+
const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin"));
|
|
15
19
|
const base_1 = require("./base");
|
|
16
20
|
const ChunkAssetPlugin_1 = tslib_1.__importDefault(require("./plugins/ChunkAssetPlugin"));
|
|
17
21
|
const utils_1 = require("./utils");
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
const CleanWebpackPlugin_1 = tslib_1.__importDefault(require("./plugins/CleanWebpackPlugin"));
|
|
23
|
+
// When building, include the plugin to force terminate building if errors
|
|
24
|
+
// happened in the client bundle.
|
|
25
|
+
class ForceTerminatePlugin {
|
|
26
|
+
apply(compiler) {
|
|
27
|
+
compiler.hooks.done.tap('client:done', (stats) => {
|
|
28
|
+
if (stats.hasErrors()) {
|
|
29
|
+
const errorsWarnings = stats.toJson('errors-warnings');
|
|
30
|
+
logger_1.default.error(`Client bundle compiled with errors therefore further build is impossible.\n${(0, utils_1.formatStatsErrorMessage)(errorsWarnings)}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function createBaseClientConfig({ props, hydrate, minify, }) {
|
|
37
|
+
const baseConfig = await (0, base_1.createBaseConfig)({ props, isServer: false, minify });
|
|
38
|
+
return (0, webpack_merge_1.default)(baseConfig, {
|
|
22
39
|
// Useless, disabled on purpose (errors on existing sites with no
|
|
23
40
|
// browserslist config)
|
|
24
41
|
// target: 'browserslist',
|
|
@@ -29,7 +46,7 @@ async function createClientConfig(props, minify = true, hydrate = true) {
|
|
|
29
46
|
runtimeChunk: true,
|
|
30
47
|
},
|
|
31
48
|
plugins: [
|
|
32
|
-
new webpack_1.DefinePlugin({
|
|
49
|
+
new webpack_1.default.DefinePlugin({
|
|
33
50
|
'process.env.HYDRATE_CLIENT_ENTRY': JSON.stringify(hydrate),
|
|
34
51
|
}),
|
|
35
52
|
new ChunkAssetPlugin_1.default(),
|
|
@@ -39,21 +56,59 @@ async function createClientConfig(props, minify = true, hydrate = true) {
|
|
|
39
56
|
}),
|
|
40
57
|
],
|
|
41
58
|
});
|
|
42
|
-
// When building, include the plugin to force terminate building if errors
|
|
43
|
-
// happened in the client bundle.
|
|
44
|
-
if (isBuilding) {
|
|
45
|
-
clientConfig.plugins?.push({
|
|
46
|
-
apply: (compiler) => {
|
|
47
|
-
compiler.hooks.done.tap('client:done', (stats) => {
|
|
48
|
-
if (stats.hasErrors()) {
|
|
49
|
-
const errorsWarnings = stats.toJson('errors-warnings');
|
|
50
|
-
logger_1.default.error(`Client bundle compiled with errors therefore further build is impossible.\n${(0, utils_1.formatStatsErrorMessage)(errorsWarnings)}`);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
return clientConfig;
|
|
58
59
|
}
|
|
59
|
-
|
|
60
|
+
// client config when running "docusaurus start"
|
|
61
|
+
async function createStartClientConfig({ props, minify, poll, }) {
|
|
62
|
+
const { siteConfig, headTags, preBodyTags, postBodyTags } = props;
|
|
63
|
+
const clientConfig = (0, webpack_merge_1.default)(await createBaseClientConfig({
|
|
64
|
+
props,
|
|
65
|
+
minify,
|
|
66
|
+
hydrate: false,
|
|
67
|
+
}), {
|
|
68
|
+
watchOptions: {
|
|
69
|
+
ignored: /node_modules\/(?!@docusaurus)/,
|
|
70
|
+
poll,
|
|
71
|
+
},
|
|
72
|
+
infrastructureLogging: {
|
|
73
|
+
// Reduce log verbosity, see https://github.com/facebook/docusaurus/pull/5420#issuecomment-906613105
|
|
74
|
+
level: 'warn',
|
|
75
|
+
},
|
|
76
|
+
plugins: [
|
|
77
|
+
// Generates an `index.html` file with the <script> injected.
|
|
78
|
+
new html_webpack_plugin_1.default({
|
|
79
|
+
template: path_1.default.join(__dirname, '../templates/dev.html.template.ejs'),
|
|
80
|
+
// So we can define the position where the scripts are injected.
|
|
81
|
+
inject: false,
|
|
82
|
+
filename: 'index.html',
|
|
83
|
+
title: siteConfig.title,
|
|
84
|
+
headTags,
|
|
85
|
+
preBodyTags,
|
|
86
|
+
postBodyTags,
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
return { clientConfig };
|
|
91
|
+
}
|
|
92
|
+
exports.createStartClientConfig = createStartClientConfig;
|
|
93
|
+
// client config when running "docusaurus build"
|
|
94
|
+
async function createBuildClientConfig({ props, minify, bundleAnalyzer, }) {
|
|
95
|
+
// Apply user webpack config.
|
|
96
|
+
const { generatedFilesDir } = props;
|
|
97
|
+
const clientManifestPath = path_1.default.join(generatedFilesDir, 'client-manifest.json');
|
|
98
|
+
const config = (0, webpack_merge_1.default)(await createBaseClientConfig({ props, minify, hydrate: true }), {
|
|
99
|
+
plugins: [
|
|
100
|
+
new ForceTerminatePlugin(),
|
|
101
|
+
// Remove/clean build folders before building bundles.
|
|
102
|
+
new CleanWebpackPlugin_1.default({ verbose: false }),
|
|
103
|
+
// Visualize size of webpack output files with an interactive zoomable
|
|
104
|
+
// tree map.
|
|
105
|
+
bundleAnalyzer && new webpack_bundle_analyzer_1.BundleAnalyzerPlugin(),
|
|
106
|
+
// Generate client manifests file that will be used for server bundle.
|
|
107
|
+
new react_loadable_ssr_addon_v5_slorber_1.default({
|
|
108
|
+
filename: clientManifestPath,
|
|
109
|
+
}),
|
|
110
|
+
].filter((x) => Boolean(x)),
|
|
111
|
+
});
|
|
112
|
+
return { config, clientManifestPath };
|
|
113
|
+
}
|
|
114
|
+
exports.createBuildClientConfig = createBuildClientConfig;
|
|
@@ -0,0 +1,8 @@
|
|
|
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 { WebpackPluginInstance } from 'webpack';
|
|
8
|
+
export declare function getMinimizer(): WebpackPluginInstance[];
|
|
@@ -0,0 +1,97 @@
|
|
|
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.getMinimizer = void 0;
|
|
10
|
+
const tslib_1 = require("tslib");
|
|
11
|
+
const terser_webpack_plugin_1 = tslib_1.__importDefault(require("terser-webpack-plugin"));
|
|
12
|
+
const css_minimizer_webpack_plugin_1 = tslib_1.__importDefault(require("css-minimizer-webpack-plugin"));
|
|
13
|
+
// See https://github.com/webpack-contrib/terser-webpack-plugin#parallel
|
|
14
|
+
function getTerserParallel() {
|
|
15
|
+
let terserParallel = true;
|
|
16
|
+
if (process.env.TERSER_PARALLEL === 'false') {
|
|
17
|
+
terserParallel = false;
|
|
18
|
+
}
|
|
19
|
+
else if (process.env.TERSER_PARALLEL &&
|
|
20
|
+
parseInt(process.env.TERSER_PARALLEL, 10) > 0) {
|
|
21
|
+
terserParallel = parseInt(process.env.TERSER_PARALLEL, 10);
|
|
22
|
+
}
|
|
23
|
+
return terserParallel;
|
|
24
|
+
}
|
|
25
|
+
function getJsMinifierPlugin() {
|
|
26
|
+
return new terser_webpack_plugin_1.default({
|
|
27
|
+
parallel: getTerserParallel(),
|
|
28
|
+
terserOptions: {
|
|
29
|
+
parse: {
|
|
30
|
+
// We want uglify-js to parse ecma 8 code. However, we don't want it
|
|
31
|
+
// to apply any minification steps that turns valid ecma 5 code
|
|
32
|
+
// into invalid ecma 5 code. This is why the 'compress' and 'output'
|
|
33
|
+
// sections only apply transformations that are ecma 5 safe
|
|
34
|
+
// https://github.com/facebook/create-react-app/pull/4234
|
|
35
|
+
ecma: 2020,
|
|
36
|
+
},
|
|
37
|
+
compress: {
|
|
38
|
+
ecma: 5,
|
|
39
|
+
},
|
|
40
|
+
mangle: {
|
|
41
|
+
safari10: true,
|
|
42
|
+
},
|
|
43
|
+
output: {
|
|
44
|
+
ecma: 5,
|
|
45
|
+
comments: false,
|
|
46
|
+
// Turned on because emoji and regex is not minified properly using
|
|
47
|
+
// default. See https://github.com/facebook/create-react-app/issues/2488
|
|
48
|
+
ascii_only: true,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function getAdvancedCssMinifier() {
|
|
54
|
+
// Using the array syntax to add 2 minimizers
|
|
55
|
+
// see https://github.com/webpack-contrib/css-minimizer-webpack-plugin#array
|
|
56
|
+
return new css_minimizer_webpack_plugin_1.default({
|
|
57
|
+
minimizerOptions: [
|
|
58
|
+
// CssNano options
|
|
59
|
+
{
|
|
60
|
+
preset: require.resolve('@docusaurus/cssnano-preset'),
|
|
61
|
+
},
|
|
62
|
+
// CleanCss options
|
|
63
|
+
{
|
|
64
|
+
inline: false,
|
|
65
|
+
level: {
|
|
66
|
+
1: {
|
|
67
|
+
all: false,
|
|
68
|
+
removeWhitespace: true,
|
|
69
|
+
},
|
|
70
|
+
2: {
|
|
71
|
+
all: true,
|
|
72
|
+
restructureRules: true,
|
|
73
|
+
removeUnusedAtRules: false,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
minify: [
|
|
79
|
+
css_minimizer_webpack_plugin_1.default.cssnanoMinify,
|
|
80
|
+
css_minimizer_webpack_plugin_1.default.cleanCssMinify,
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
function getMinimizer() {
|
|
85
|
+
// This is an historical env variable to opt-out of the advanced minifier
|
|
86
|
+
// Sometimes there's a bug in it and people are happy to disable it
|
|
87
|
+
const useSimpleCssMinifier = process.env.USE_SIMPLE_CSS_MINIFIER === 'true';
|
|
88
|
+
const minimizer = [getJsMinifierPlugin()];
|
|
89
|
+
if (useSimpleCssMinifier) {
|
|
90
|
+
minimizer.push(new css_minimizer_webpack_plugin_1.default());
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
minimizer.push(getAdvancedCssMinifier());
|
|
94
|
+
}
|
|
95
|
+
return minimizer;
|
|
96
|
+
}
|
|
97
|
+
exports.getMinimizer = getMinimizer;
|
package/lib/webpack/server.d.ts
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
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 Locals } from '@slorber/static-site-generator-webpack-plugin';
|
|
8
7
|
import type { Props } from '@docusaurus/types';
|
|
9
8
|
import type { Configuration } from 'webpack';
|
|
10
|
-
export default function createServerConfig(
|
|
9
|
+
export default function createServerConfig(params: {
|
|
11
10
|
props: Props;
|
|
12
|
-
}): Promise<
|
|
11
|
+
}): Promise<{
|
|
12
|
+
config: Configuration;
|
|
13
|
+
serverBundlePath: string;
|
|
14
|
+
}>;
|