@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/bin/docusaurus.mjs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
// @ts-check
|
|
10
10
|
|
|
11
|
+
import {inspect} from 'node:util';
|
|
11
12
|
import logger from '@docusaurus/logger';
|
|
12
13
|
import cli from 'commander';
|
|
13
14
|
import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
|
|
@@ -61,8 +62,6 @@ cli
|
|
|
61
62
|
'--no-minify',
|
|
62
63
|
'build website without minimizing JS bundles (default: false)',
|
|
63
64
|
)
|
|
64
|
-
// @ts-expect-error: Promise<string> is not assignable to Promise<void>... but
|
|
65
|
-
// good enough here.
|
|
66
65
|
.action(build);
|
|
67
66
|
|
|
68
67
|
cli
|
|
@@ -86,6 +85,10 @@ cli
|
|
|
86
85
|
'-t, --typescript',
|
|
87
86
|
'copy TypeScript theme files when possible (default: false)',
|
|
88
87
|
)
|
|
88
|
+
.option(
|
|
89
|
+
'-j, --javascript',
|
|
90
|
+
'copy JavaScript theme files when possible (default: false)',
|
|
91
|
+
)
|
|
89
92
|
.option('--danger', 'enable swizzle for unsafe component of themes')
|
|
90
93
|
.option(
|
|
91
94
|
'--config <config>',
|
|
@@ -269,9 +272,11 @@ cli.parse(process.argv);
|
|
|
269
272
|
|
|
270
273
|
process.on('unhandledRejection', (err) => {
|
|
271
274
|
console.log('');
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
+
|
|
276
|
+
// We need to use inspect with increased depth to log the full causal chain
|
|
277
|
+
// By default Node logging has depth=2
|
|
278
|
+
// see also https://github.com/nodejs/node/issues/51637
|
|
279
|
+
logger.error(inspect(err, {depth: Infinity}));
|
|
275
280
|
|
|
276
281
|
logger.info`Docusaurus version: number=${DOCUSAURUS_VERSION}
|
|
277
282
|
Node version: number=${process.version}`;
|
|
@@ -11,10 +11,10 @@ export const createStatefulBrokenLinks = () => {
|
|
|
11
11
|
const allLinks = new Set();
|
|
12
12
|
return {
|
|
13
13
|
collectAnchor: (anchor) => {
|
|
14
|
-
allAnchors.add(anchor);
|
|
14
|
+
typeof anchor !== 'undefined' && allAnchors.add(anchor);
|
|
15
15
|
},
|
|
16
16
|
collectLink: (link) => {
|
|
17
|
-
allLinks.add(link);
|
|
17
|
+
typeof link !== 'undefined' && allLinks.add(link);
|
|
18
18
|
},
|
|
19
19
|
getCollectedAnchors: () => [...allAnchors],
|
|
20
20
|
getCollectedLinks: () => [...allLinks],
|
|
@@ -97,11 +97,19 @@ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLi
|
|
|
97
97
|
}
|
|
98
98
|
};
|
|
99
99
|
}, [ioRef, targetLink, IOSupported, isInternal]);
|
|
100
|
+
// It is simple local anchor link targeting current page?
|
|
100
101
|
const isAnchorLink = targetLink?.startsWith('#') ?? false;
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
// See also RR logic:
|
|
103
|
+
// https://github.com/remix-run/react-router/blob/v5/packages/react-router-dom/modules/Link.js#L47
|
|
104
|
+
const hasInternalTarget = !props.target || props.target === '_self';
|
|
105
|
+
// Should we use a regular <a> tag instead of React-Router Link component?
|
|
106
|
+
const isRegularHtmlLink = !targetLink || !isInternal || !hasInternalTarget || isAnchorLink;
|
|
107
|
+
if (!noBrokenLinkCheck && (isAnchorLink || !isRegularHtmlLink)) {
|
|
103
108
|
brokenLinks.collectLink(targetLink);
|
|
104
109
|
}
|
|
110
|
+
if (props.id) {
|
|
111
|
+
brokenLinks.collectAnchor(props.id);
|
|
112
|
+
}
|
|
105
113
|
return isRegularHtmlLink ? (
|
|
106
114
|
// eslint-disable-next-line jsx-a11y/anchor-has-content, @docusaurus/no-html-links
|
|
107
115
|
<a ref={innerRef} href={targetLink} {...(targetLinkUnprefixed &&
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { renderToPipeableStream } from 'react-dom/server';
|
|
8
8
|
import { Writable } from 'stream';
|
|
9
|
-
export async function
|
|
9
|
+
export async function renderToHtml(app) {
|
|
10
10
|
// Inspired from
|
|
11
11
|
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
|
|
12
12
|
// https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/static-entry.js
|
|
@@ -4,7 +4,6 @@
|
|
|
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 {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}): Promise<string>;
|
|
7
|
+
import type { AppRenderer } from '../common';
|
|
8
|
+
declare const render: AppRenderer;
|
|
9
|
+
export default render;
|
|
@@ -5,59 +5,15 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
7
|
import React from 'react';
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import fs from 'fs-extra';
|
|
10
|
-
// eslint-disable-next-line no-restricted-imports
|
|
11
|
-
import _ from 'lodash';
|
|
12
|
-
import * as eta from 'eta';
|
|
13
8
|
import { StaticRouter } from 'react-router-dom';
|
|
14
9
|
import { HelmetProvider } from 'react-helmet-async';
|
|
15
|
-
import { getBundles } from 'react-loadable-ssr-addon-v5-slorber';
|
|
16
10
|
import Loadable from 'react-loadable';
|
|
17
|
-
import {
|
|
18
|
-
import { renderStaticApp } from './serverRenderer';
|
|
11
|
+
import { renderToHtml } from './renderToHtml';
|
|
19
12
|
import preload from './preload';
|
|
20
13
|
import App from './App';
|
|
21
14
|
import { createStatefulBrokenLinks, BrokenLinksProvider, } from './BrokenLinksContext';
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
}));
|
|
25
|
-
function renderSSRTemplate(ssrTemplate, data) {
|
|
26
|
-
const compiled = getCompiledSSRTemplate(ssrTemplate);
|
|
27
|
-
return compiled(data, eta.defaultConfig);
|
|
28
|
-
}
|
|
29
|
-
function buildSSRErrorMessage({ error, pathname, }) {
|
|
30
|
-
const parts = [
|
|
31
|
-
`Docusaurus server-side rendering could not render static page with path ${pathname} because of error: ${error.message}`,
|
|
32
|
-
];
|
|
33
|
-
const isNotDefinedErrorRegex = /(?:window|document|localStorage|navigator|alert|location|buffer|self) is not defined/i;
|
|
34
|
-
if (isNotDefinedErrorRegex.test(error.message)) {
|
|
35
|
-
// prettier-ignore
|
|
36
|
-
parts.push(`It looks like you are using code that should run on the client-side only.
|
|
37
|
-
To get around it, try using \`<BrowserOnly>\` (https://docusaurus.io/docs/docusaurus-core/#browseronly) or \`ExecutionEnvironment\` (https://docusaurus.io/docs/docusaurus-core/#executionenvironment).
|
|
38
|
-
It might also require to wrap your client code in \`useEffect\` hook and/or import a third-party library dynamically (if any).`);
|
|
39
|
-
}
|
|
40
|
-
return parts.join('\n');
|
|
41
|
-
}
|
|
42
|
-
export default async function render(locals) {
|
|
43
|
-
try {
|
|
44
|
-
return await doRender(locals);
|
|
45
|
-
}
|
|
46
|
-
catch (errorUnknown) {
|
|
47
|
-
const error = errorUnknown;
|
|
48
|
-
const message = buildSSRErrorMessage({ error, pathname: locals.path });
|
|
49
|
-
const ssrError = new Error(message, { cause: error });
|
|
50
|
-
// It is important to log the error here because the stacktrace causal chain
|
|
51
|
-
// is not available anymore upper in the tree (this SSR runs in eval)
|
|
52
|
-
console.error(ssrError);
|
|
53
|
-
throw ssrError;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// Renderer for static-site-generator-webpack-plugin (async rendering).
|
|
57
|
-
async function doRender(locals) {
|
|
58
|
-
const { routesLocation, headTags, preBodyTags, postBodyTags, onLinksCollected, onHeadTagsCollected, baseUrl, ssrTemplate, noIndex, DOCUSAURUS_VERSION, } = locals;
|
|
59
|
-
const location = routesLocation[locals.path];
|
|
60
|
-
await preload(location);
|
|
15
|
+
const render = async ({ pathname }) => {
|
|
16
|
+
await preload(pathname);
|
|
61
17
|
const modules = new Set();
|
|
62
18
|
const routerContext = {};
|
|
63
19
|
const helmetContext = {};
|
|
@@ -66,77 +22,20 @@ async function doRender(locals) {
|
|
|
66
22
|
// @ts-expect-error: we are migrating away from react-loadable anyways
|
|
67
23
|
<Loadable.Capture report={(moduleName) => modules.add(moduleName)}>
|
|
68
24
|
<HelmetProvider context={helmetContext}>
|
|
69
|
-
<StaticRouter location={
|
|
25
|
+
<StaticRouter location={pathname} context={routerContext}>
|
|
70
26
|
<BrokenLinksProvider brokenLinks={statefulBrokenLinks}>
|
|
71
27
|
<App />
|
|
72
28
|
</BrokenLinksProvider>
|
|
73
29
|
</StaticRouter>
|
|
74
30
|
</HelmetProvider>
|
|
75
31
|
</Loadable.Capture>);
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
32
|
+
const html = await renderToHtml(app);
|
|
33
|
+
const collectedData = {
|
|
34
|
+
helmet: helmetContext.helmet,
|
|
79
35
|
anchors: statefulBrokenLinks.getCollectedAnchors(),
|
|
80
36
|
links: statefulBrokenLinks.getCollectedLinks(),
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
helmet.title.toString(),
|
|
87
|
-
helmet.meta.toString(),
|
|
88
|
-
helmet.link.toString(),
|
|
89
|
-
helmet.script.toString(),
|
|
90
|
-
];
|
|
91
|
-
onHeadTagsCollected(location, helmet);
|
|
92
|
-
const metaAttributes = metaStrings.filter(Boolean);
|
|
93
|
-
const { generatedFilesDir } = locals;
|
|
94
|
-
const manifestPath = path.join(generatedFilesDir, 'client-manifest.json');
|
|
95
|
-
// Using readJSON seems to fail for users of some plugins, possibly because of
|
|
96
|
-
// the eval sandbox having a different `Buffer` instance (native one instead
|
|
97
|
-
// of polyfilled one)
|
|
98
|
-
const manifest = (await fs
|
|
99
|
-
.readFile(manifestPath, 'utf-8')
|
|
100
|
-
.then(JSON.parse));
|
|
101
|
-
// Get all required assets for this particular page based on client
|
|
102
|
-
// manifest information.
|
|
103
|
-
const modulesToBeLoaded = [...manifest.entrypoints, ...Array.from(modules)];
|
|
104
|
-
const bundles = getBundles(manifest, modulesToBeLoaded);
|
|
105
|
-
const stylesheets = (bundles.css ?? []).map((b) => b.file);
|
|
106
|
-
const scripts = (bundles.js ?? []).map((b) => b.file);
|
|
107
|
-
const renderedHtml = renderSSRTemplate(ssrTemplate, {
|
|
108
|
-
appHtml,
|
|
109
|
-
baseUrl,
|
|
110
|
-
htmlAttributes,
|
|
111
|
-
bodyAttributes,
|
|
112
|
-
headTags,
|
|
113
|
-
preBodyTags,
|
|
114
|
-
postBodyTags,
|
|
115
|
-
metaAttributes,
|
|
116
|
-
scripts,
|
|
117
|
-
stylesheets,
|
|
118
|
-
noIndex,
|
|
119
|
-
version: DOCUSAURUS_VERSION,
|
|
120
|
-
});
|
|
121
|
-
try {
|
|
122
|
-
if (process.env.SKIP_HTML_MINIFICATION === 'true') {
|
|
123
|
-
return renderedHtml;
|
|
124
|
-
}
|
|
125
|
-
// Minify html with https://github.com/DanielRuf/html-minifier-terser
|
|
126
|
-
return await minify(renderedHtml, {
|
|
127
|
-
removeComments: false,
|
|
128
|
-
removeRedundantAttributes: true,
|
|
129
|
-
removeEmptyAttributes: true,
|
|
130
|
-
removeScriptTypeAttributes: true,
|
|
131
|
-
removeStyleLinkTypeAttributes: true,
|
|
132
|
-
useShortDoctype: true,
|
|
133
|
-
minifyJS: true,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
catch (err) {
|
|
137
|
-
// prettier-ignore
|
|
138
|
-
console.error(`Minification of page ${locals.path} failed.`);
|
|
139
|
-
console.error(err);
|
|
140
|
-
throw err;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
37
|
+
modules: Array.from(modules),
|
|
38
|
+
};
|
|
39
|
+
return { html, collectedData };
|
|
40
|
+
};
|
|
41
|
+
export default render;
|
|
@@ -11,6 +11,7 @@ import Head from '@docusaurus/Head';
|
|
|
11
11
|
import ErrorBoundary from '@docusaurus/ErrorBoundary';
|
|
12
12
|
import { getErrorCausalChain } from '@docusaurus/utils-common';
|
|
13
13
|
import Layout from '@theme/Layout';
|
|
14
|
+
import { RouteContextProvider } from '../../routeContext';
|
|
14
15
|
function ErrorDisplay({ error, tryAgain }) {
|
|
15
16
|
return (<div style={{
|
|
16
17
|
display: 'flex',
|
|
@@ -42,19 +43,32 @@ function ErrorBoundaryError({ error }) {
|
|
|
42
43
|
const fullMessage = causalChain.map((e) => e.message).join('\n\nCause:\n');
|
|
43
44
|
return <p style={{ whiteSpace: 'pre-wrap' }}>{fullMessage}</p>;
|
|
44
45
|
}
|
|
46
|
+
// A bit hacky: we need to add an artificial RouteContextProvider here
|
|
47
|
+
// The goal is to be able to render the error inside the theme layout
|
|
48
|
+
// Without this, our theme classic would crash due to lack of route context
|
|
49
|
+
// See also https://github.com/facebook/docusaurus/pull/9852
|
|
50
|
+
function ErrorRouteContextProvider({ children }) {
|
|
51
|
+
return (<RouteContextProvider value={{
|
|
52
|
+
plugin: { name: 'docusaurus-core-error-boundary', id: 'default' },
|
|
53
|
+
}}>
|
|
54
|
+
{children}
|
|
55
|
+
</RouteContextProvider>);
|
|
56
|
+
}
|
|
45
57
|
export default function Error({ error, tryAgain }) {
|
|
46
58
|
// We wrap the error in its own error boundary because the layout can actually
|
|
47
59
|
// throw too... Only the ErrorDisplay component is simple enough to be
|
|
48
60
|
// considered safe to never throw
|
|
49
|
-
return (<
|
|
61
|
+
return (<ErrorRouteContextProvider>
|
|
62
|
+
<ErrorBoundary
|
|
50
63
|
// Note: we display the original error here, not the error that we
|
|
51
64
|
// captured in this extra error boundary
|
|
52
65
|
fallback={() => <ErrorDisplay error={error} tryAgain={tryAgain}/>}>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
66
|
+
<Head>
|
|
67
|
+
<title>Page Error</title>
|
|
68
|
+
</Head>
|
|
69
|
+
<Layout>
|
|
70
|
+
<ErrorDisplay error={error} tryAgain={tryAgain}/>
|
|
71
|
+
</Layout>
|
|
72
|
+
</ErrorBoundary>
|
|
73
|
+
</ErrorRouteContextProvider>);
|
|
60
74
|
}
|
package/lib/commands/build.d.ts
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
* This source code is licensed under the MIT license found in the
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*/
|
|
7
|
-
import { type
|
|
8
|
-
export type BuildCLIOptions = Pick<
|
|
7
|
+
import { type LoadContextParams } from '../server/site';
|
|
8
|
+
export type BuildCLIOptions = Pick<LoadContextParams, 'config' | 'locale' | 'outDir'> & {
|
|
9
9
|
bundleAnalyzer?: boolean;
|
|
10
10
|
minify?: boolean;
|
|
11
11
|
dev?: boolean;
|
|
12
12
|
};
|
|
13
|
-
export declare function build(siteDirParam?: string, cliOptions?: Partial<BuildCLIOptions>, forceTerminate?: boolean): Promise<
|
|
13
|
+
export declare function build(siteDirParam?: string, cliOptions?: Partial<BuildCLIOptions>, forceTerminate?: boolean): Promise<void>;
|
package/lib/commands/build.js
CHANGED
|
@@ -10,19 +10,19 @@ exports.build = 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 lodash_1 = tslib_1.__importDefault(require("lodash"));
|
|
13
14
|
const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
|
|
14
15
|
const utils_1 = require("@docusaurus/utils");
|
|
15
|
-
const
|
|
16
|
-
const react_loadable_ssr_addon_v5_slorber_1 = tslib_1.__importDefault(require("react-loadable-ssr-addon-v5-slorber"));
|
|
17
|
-
const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
|
|
18
|
-
const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
|
|
19
|
-
const server_1 = require("../server");
|
|
16
|
+
const site_1 = require("../server/site");
|
|
20
17
|
const brokenLinks_1 = require("../server/brokenLinks");
|
|
21
|
-
const client_1 =
|
|
22
|
-
const
|
|
18
|
+
const client_1 = require("../webpack/client");
|
|
19
|
+
const server_1 = tslib_1.__importDefault(require("../webpack/server"));
|
|
23
20
|
const utils_2 = require("../webpack/utils");
|
|
24
|
-
const
|
|
21
|
+
const utils_3 = require("../utils");
|
|
25
22
|
const i18n_1 = require("../server/i18n");
|
|
23
|
+
const ssg_1 = require("../ssg");
|
|
24
|
+
const templates_1 = require("../templates/templates");
|
|
25
|
+
const ssr_html_template_1 = tslib_1.__importDefault(require("../templates/ssr.html.template"));
|
|
26
26
|
async function build(siteDirParam = '.', cliOptions = {},
|
|
27
27
|
// When running build, we force terminate the process to prevent async
|
|
28
28
|
// operations from never returning. However, if run as part of docusaurus
|
|
@@ -41,15 +41,13 @@ forceTerminate = true) {
|
|
|
41
41
|
['SIGINT', 'SIGTERM'].forEach((sig) => {
|
|
42
42
|
process.on(sig, () => process.exit());
|
|
43
43
|
});
|
|
44
|
-
async function tryToBuildLocale({ locale
|
|
44
|
+
async function tryToBuildLocale({ locale }) {
|
|
45
45
|
try {
|
|
46
|
-
|
|
46
|
+
await utils_3.PerfLogger.async(`${logger_1.default.name(locale)}`, () => buildLocale({
|
|
47
47
|
siteDir,
|
|
48
48
|
locale,
|
|
49
49
|
cliOptions,
|
|
50
|
-
|
|
51
|
-
isLastLocale,
|
|
52
|
-
});
|
|
50
|
+
}));
|
|
53
51
|
}
|
|
54
52
|
catch (err) {
|
|
55
53
|
throw new Error(logger_1.default.interpolate `Unable to build website for locale name=${locale}.`, {
|
|
@@ -57,7 +55,28 @@ forceTerminate = true) {
|
|
|
57
55
|
});
|
|
58
56
|
}
|
|
59
57
|
}
|
|
60
|
-
const
|
|
58
|
+
const locales = await utils_3.PerfLogger.async('Get locales to build', () => getLocalesToBuild({ siteDir, cliOptions }));
|
|
59
|
+
if (locales.length > 1) {
|
|
60
|
+
logger_1.default.info `Website will be built for all these locales: ${locales}`;
|
|
61
|
+
}
|
|
62
|
+
await utils_3.PerfLogger.async(`Build`, () => (0, utils_1.mapAsyncSequential)(locales, async (locale) => {
|
|
63
|
+
const isLastLocale = locales.indexOf(locale) === locales.length - 1;
|
|
64
|
+
await tryToBuildLocale({ locale });
|
|
65
|
+
if (isLastLocale) {
|
|
66
|
+
logger_1.default.info `Use code=${'npm run serve'} command to test your build locally.`;
|
|
67
|
+
}
|
|
68
|
+
// TODO do we really need this historical forceTerminate exit???
|
|
69
|
+
if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
exports.build = build;
|
|
75
|
+
async function getLocalesToBuild({ siteDir, cliOptions, }) {
|
|
76
|
+
if (cliOptions.locale) {
|
|
77
|
+
return [cliOptions.locale];
|
|
78
|
+
}
|
|
79
|
+
const context = await (0, site_1.loadContext)({
|
|
61
80
|
siteDir,
|
|
62
81
|
outDir: cliOptions.outDir,
|
|
63
82
|
config: cliOptions.config,
|
|
@@ -67,140 +86,142 @@ forceTerminate = true) {
|
|
|
67
86
|
const i18n = await (0, i18n_1.loadI18n)(context.siteConfig, {
|
|
68
87
|
locale: cliOptions.locale,
|
|
69
88
|
});
|
|
70
|
-
if (cliOptions.locale) {
|
|
71
|
-
return tryToBuildLocale({ locale: cliOptions.locale, isLastLocale: true });
|
|
72
|
-
}
|
|
73
89
|
if (i18n.locales.length > 1) {
|
|
74
90
|
logger_1.default.info `Website will be built for all these locales: ${i18n.locales}`;
|
|
75
91
|
}
|
|
76
92
|
// We need the default locale to always be the 1st in the list. If we build it
|
|
77
93
|
// last, it would "erase" the localized sites built in sub-folders
|
|
78
|
-
|
|
94
|
+
return [
|
|
79
95
|
i18n.defaultLocale,
|
|
80
96
|
...i18n.locales.filter((locale) => locale !== i18n.defaultLocale),
|
|
81
97
|
];
|
|
82
|
-
const results = await (0, utils_1.mapAsyncSequential)(orderedLocales, (locale) => {
|
|
83
|
-
const isLastLocale = orderedLocales.indexOf(locale) === orderedLocales.length - 1;
|
|
84
|
-
return tryToBuildLocale({ locale, isLastLocale });
|
|
85
|
-
});
|
|
86
|
-
return results[0];
|
|
87
98
|
}
|
|
88
|
-
|
|
89
|
-
async function buildLocale({ siteDir, locale, cliOptions, forceTerminate, isLastLocale, }) {
|
|
99
|
+
async function buildLocale({ siteDir, locale, cliOptions, }) {
|
|
90
100
|
// Temporary workaround to unlock the ability to translate the site config
|
|
91
101
|
// We'll remove it if a better official API can be designed
|
|
92
102
|
// See https://github.com/facebook/docusaurus/issues/4542
|
|
93
103
|
process.env.DOCUSAURUS_CURRENT_LOCALE = locale;
|
|
94
104
|
logger_1.default.info `name=${`[${locale}]`} Creating an optimized production build...`;
|
|
95
|
-
const
|
|
105
|
+
const site = await utils_3.PerfLogger.async('Load site', () => (0, site_1.loadSite)({
|
|
96
106
|
siteDir,
|
|
97
107
|
outDir: cliOptions.outDir,
|
|
98
108
|
config: cliOptions.config,
|
|
99
109
|
locale,
|
|
100
110
|
localizePath: cliOptions.locale ? false : undefined,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const { outDir,
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
filename: clientManifestPath,
|
|
115
|
-
}),
|
|
116
|
-
].filter((x) => Boolean(x)),
|
|
117
|
-
});
|
|
118
|
-
const collectedLinks = {};
|
|
119
|
-
const headTags = {};
|
|
120
|
-
let serverConfig = await (0, server_2.default)({
|
|
121
|
-
props,
|
|
122
|
-
onLinksCollected: ({ staticPagePath, links, anchors }) => {
|
|
123
|
-
collectedLinks[staticPagePath] = { links, anchors };
|
|
124
|
-
},
|
|
125
|
-
onHeadTagsCollected: (staticPagePath, tags) => {
|
|
126
|
-
headTags[staticPagePath] = tags;
|
|
127
|
-
},
|
|
128
|
-
});
|
|
129
|
-
// The staticDirectories option can contain empty directories, or non-existent
|
|
130
|
-
// directories (e.g. user deleted `static`). Instead of issuing an error, we
|
|
131
|
-
// just silently filter them out, because user could have never configured it
|
|
132
|
-
// in the first place (the default option should always "work").
|
|
133
|
-
const staticDirectories = (await Promise.all(staticDirectoriesOption.map(async (dir) => {
|
|
134
|
-
const staticDir = path_1.default.resolve(siteDir, dir);
|
|
135
|
-
if ((await fs_extra_1.default.pathExists(staticDir)) &&
|
|
136
|
-
(await fs_extra_1.default.readdir(staticDir)).length > 0) {
|
|
137
|
-
return staticDir;
|
|
138
|
-
}
|
|
139
|
-
return '';
|
|
140
|
-
}))).filter(Boolean);
|
|
141
|
-
if (staticDirectories.length > 0) {
|
|
142
|
-
serverConfig = (0, webpack_merge_1.default)(serverConfig, {
|
|
143
|
-
plugins: [
|
|
144
|
-
new copy_webpack_plugin_1.default({
|
|
145
|
-
patterns: staticDirectories.map((dir) => ({
|
|
146
|
-
from: dir,
|
|
147
|
-
to: outDir,
|
|
148
|
-
toType: 'dir',
|
|
149
|
-
})),
|
|
150
|
-
}),
|
|
151
|
-
],
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
// Plugin Lifecycle - configureWebpack and configurePostCss.
|
|
155
|
-
plugins.forEach((plugin) => {
|
|
156
|
-
const { configureWebpack, configurePostCss } = plugin;
|
|
157
|
-
if (configurePostCss) {
|
|
158
|
-
clientConfig = (0, utils_2.applyConfigurePostCss)(configurePostCss.bind(plugin), clientConfig);
|
|
159
|
-
}
|
|
160
|
-
if (configureWebpack) {
|
|
161
|
-
clientConfig = (0, utils_2.applyConfigureWebpack)(configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
|
162
|
-
clientConfig, false, props.siteConfig.webpack?.jsLoader, plugin.content);
|
|
163
|
-
serverConfig = (0, utils_2.applyConfigureWebpack)(configureWebpack.bind(plugin), // The plugin lifecycle may reference `this`.
|
|
164
|
-
serverConfig, true, props.siteConfig.webpack?.jsLoader, plugin.content);
|
|
165
|
-
}
|
|
166
|
-
});
|
|
167
|
-
// Make sure generated client-manifest is cleaned first so we don't reuse
|
|
168
|
-
// the one from previous builds.
|
|
169
|
-
if (await fs_extra_1.default.pathExists(clientManifestPath)) {
|
|
170
|
-
await fs_extra_1.default.unlink(clientManifestPath);
|
|
171
|
-
}
|
|
111
|
+
}));
|
|
112
|
+
const { props } = site;
|
|
113
|
+
const { outDir, plugins } = props;
|
|
114
|
+
// We can build the 2 configs in parallel
|
|
115
|
+
const [{ clientConfig, clientManifestPath }, { serverConfig, serverBundlePath }] = await utils_3.PerfLogger.async('Creating webpack configs', () => Promise.all([
|
|
116
|
+
getBuildClientConfig({
|
|
117
|
+
props,
|
|
118
|
+
cliOptions,
|
|
119
|
+
}),
|
|
120
|
+
getBuildServerConfig({
|
|
121
|
+
props,
|
|
122
|
+
}),
|
|
123
|
+
]));
|
|
172
124
|
// Run webpack to build JS bundle (client) and static html files (server).
|
|
173
|
-
await (0, utils_2.compile)([clientConfig, serverConfig]);
|
|
125
|
+
await utils_3.PerfLogger.async('Bundling with Webpack', () => (0, utils_2.compile)([clientConfig, serverConfig]));
|
|
126
|
+
const { collectedData } = await utils_3.PerfLogger.async('SSG', () => executeSSG({
|
|
127
|
+
props,
|
|
128
|
+
serverBundlePath,
|
|
129
|
+
clientManifestPath,
|
|
130
|
+
}));
|
|
174
131
|
// Remove server.bundle.js because it is not needed.
|
|
175
|
-
|
|
176
|
-
const serverBundle = path_1.default.join(outDir, serverConfig.output.filename);
|
|
177
|
-
if (await fs_extra_1.default.pathExists(serverBundle)) {
|
|
178
|
-
await fs_extra_1.default.unlink(serverBundle);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
132
|
+
await utils_3.PerfLogger.async('Deleting server bundle', () => ensureUnlink(serverBundlePath));
|
|
181
133
|
// Plugin Lifecycle - postBuild.
|
|
134
|
+
await utils_3.PerfLogger.async('postBuild()', () => executePluginsPostBuild({ plugins, props, collectedData }));
|
|
135
|
+
// TODO execute this in parallel to postBuild?
|
|
136
|
+
await utils_3.PerfLogger.async('Broken links checker', () => executeBrokenLinksCheck({ props, collectedData }));
|
|
137
|
+
logger_1.default.success `Generated static files in path=${path_1.default.relative(process.cwd(), outDir)}.`;
|
|
138
|
+
return outDir;
|
|
139
|
+
}
|
|
140
|
+
async function executeSSG({ props, serverBundlePath, clientManifestPath, }) {
|
|
141
|
+
const manifest = await utils_3.PerfLogger.async('Read client manifest', () => fs_extra_1.default.readJSON(clientManifestPath, 'utf-8'));
|
|
142
|
+
const ssrTemplate = await utils_3.PerfLogger.async('Compile SSR template', () => (0, templates_1.compileSSRTemplate)(props.siteConfig.ssrTemplate ?? ssr_html_template_1.default));
|
|
143
|
+
const renderer = await utils_3.PerfLogger.async('Load App renderer', () => (0, ssg_1.loadAppRenderer)({
|
|
144
|
+
serverBundlePath,
|
|
145
|
+
}));
|
|
146
|
+
const ssgResult = await utils_3.PerfLogger.async('Generate static files', () => (0, ssg_1.generateStaticFiles)({
|
|
147
|
+
pathnames: props.routesPaths,
|
|
148
|
+
renderer,
|
|
149
|
+
params: {
|
|
150
|
+
trailingSlash: props.siteConfig.trailingSlash,
|
|
151
|
+
outDir: props.outDir,
|
|
152
|
+
baseUrl: props.baseUrl,
|
|
153
|
+
manifest,
|
|
154
|
+
headTags: props.headTags,
|
|
155
|
+
preBodyTags: props.preBodyTags,
|
|
156
|
+
postBodyTags: props.postBodyTags,
|
|
157
|
+
ssrTemplate,
|
|
158
|
+
noIndex: props.siteConfig.noIndex,
|
|
159
|
+
DOCUSAURUS_VERSION: utils_1.DOCUSAURUS_VERSION,
|
|
160
|
+
},
|
|
161
|
+
}));
|
|
162
|
+
return ssgResult;
|
|
163
|
+
}
|
|
164
|
+
async function executePluginsPostBuild({ plugins, props, collectedData, }) {
|
|
165
|
+
const head = lodash_1.default.mapValues(collectedData, (d) => d.helmet);
|
|
182
166
|
await Promise.all(plugins.map(async (plugin) => {
|
|
183
167
|
if (!plugin.postBuild) {
|
|
184
168
|
return;
|
|
185
169
|
}
|
|
186
170
|
await plugin.postBuild({
|
|
187
171
|
...props,
|
|
188
|
-
head
|
|
172
|
+
head,
|
|
189
173
|
content: plugin.content,
|
|
190
174
|
});
|
|
191
175
|
}));
|
|
176
|
+
}
|
|
177
|
+
async function executeBrokenLinksCheck({ props: { routes, siteConfig: { onBrokenLinks, onBrokenAnchors }, }, collectedData, }) {
|
|
178
|
+
const collectedLinks = lodash_1.default.mapValues(collectedData, (d) => ({
|
|
179
|
+
links: d.links,
|
|
180
|
+
anchors: d.anchors,
|
|
181
|
+
}));
|
|
192
182
|
await (0, brokenLinks_1.handleBrokenLinks)({
|
|
193
183
|
collectedLinks,
|
|
194
184
|
routes,
|
|
195
185
|
onBrokenLinks,
|
|
196
186
|
onBrokenAnchors,
|
|
197
187
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
188
|
+
}
|
|
189
|
+
async function getBuildClientConfig({ props, cliOptions, }) {
|
|
190
|
+
const { plugins } = props;
|
|
191
|
+
const result = await (0, client_1.createBuildClientConfig)({
|
|
192
|
+
props,
|
|
193
|
+
minify: cliOptions.minify ?? true,
|
|
194
|
+
bundleAnalyzer: cliOptions.bundleAnalyzer ?? false,
|
|
195
|
+
});
|
|
196
|
+
let { config } = result;
|
|
197
|
+
config = (0, utils_2.executePluginsConfigureWebpack)({
|
|
198
|
+
plugins,
|
|
199
|
+
config,
|
|
200
|
+
isServer: false,
|
|
201
|
+
jsLoader: props.siteConfig.webpack?.jsLoader,
|
|
202
|
+
});
|
|
203
|
+
return { clientConfig: config, clientManifestPath: result.clientManifestPath };
|
|
204
|
+
}
|
|
205
|
+
async function getBuildServerConfig({ props }) {
|
|
206
|
+
const { plugins } = props;
|
|
207
|
+
const result = await (0, server_1.default)({
|
|
208
|
+
props,
|
|
209
|
+
});
|
|
210
|
+
let { config } = result;
|
|
211
|
+
config = (0, utils_2.executePluginsConfigurePostCss)({
|
|
212
|
+
plugins,
|
|
213
|
+
config,
|
|
214
|
+
});
|
|
215
|
+
config = (0, utils_2.executePluginsConfigureWebpack)({
|
|
216
|
+
plugins,
|
|
217
|
+
config,
|
|
218
|
+
isServer: true,
|
|
219
|
+
jsLoader: props.siteConfig.webpack?.jsLoader,
|
|
220
|
+
});
|
|
221
|
+
return { serverConfig: config, serverBundlePath: result.serverBundlePath };
|
|
222
|
+
}
|
|
223
|
+
async function ensureUnlink(filepath) {
|
|
224
|
+
if (await fs_extra_1.default.pathExists(filepath)) {
|
|
225
|
+
await fs_extra_1.default.unlink(filepath);
|
|
204
226
|
}
|
|
205
|
-
return outDir;
|
|
206
227
|
}
|
package/lib/commands/deploy.d.ts
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
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
|
|
8
|
-
export type DeployCLIOptions = Pick<
|
|
7
|
+
import { type LoadContextParams } from '../server/site';
|
|
8
|
+
export type DeployCLIOptions = Pick<LoadContextParams, 'config' | 'locale' | 'outDir'> & {
|
|
9
9
|
skipBuild?: boolean;
|
|
10
10
|
};
|
|
11
11
|
export declare function deploy(siteDirParam?: string, cliOptions?: Partial<DeployCLIOptions>): Promise<void>;
|