@docusaurus/core 3.1.1 → 3.2.1

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.
Files changed (85) hide show
  1. package/bin/docusaurus.mjs +10 -5
  2. package/lib/client/{serverRenderer.d.ts → renderToHtml.d.ts} +1 -1
  3. package/lib/client/{serverRenderer.js → renderToHtml.js} +1 -1
  4. package/lib/client/serverEntry.d.ts +3 -4
  5. package/lib/client/serverEntry.js +12 -113
  6. package/lib/client/theme-fallback/Error/index.js +22 -8
  7. package/lib/commands/build.d.ts +3 -3
  8. package/lib/commands/build.js +133 -112
  9. package/lib/commands/deploy.d.ts +2 -2
  10. package/lib/commands/deploy.js +3 -3
  11. package/lib/commands/external.js +2 -2
  12. package/lib/commands/serve.d.ts +2 -2
  13. package/lib/commands/serve.js +4 -4
  14. package/lib/commands/{start.d.ts → start/start.d.ts} +3 -3
  15. package/lib/commands/start/start.js +47 -0
  16. package/lib/commands/start/utils.d.ts +31 -0
  17. package/lib/commands/start/utils.js +87 -0
  18. package/lib/commands/start/watcher.d.ts +42 -0
  19. package/lib/commands/start/watcher.js +78 -0
  20. package/lib/commands/start/webpack.d.ts +15 -0
  21. package/lib/commands/start/webpack.js +133 -0
  22. package/lib/commands/swizzle/common.d.ts +1 -0
  23. package/lib/commands/swizzle/common.js +1 -0
  24. package/lib/commands/swizzle/context.js +2 -2
  25. package/lib/commands/swizzle/index.js +32 -3
  26. package/lib/commands/writeHeadingIds.js +2 -2
  27. package/lib/commands/writeTranslations.d.ts +2 -2
  28. package/lib/commands/writeTranslations.js +3 -3
  29. package/lib/index.d.ts +1 -1
  30. package/lib/index.js +1 -1
  31. package/lib/server/brokenLinks.js +4 -4
  32. package/lib/server/clientModules.d.ts +1 -1
  33. package/lib/server/clientModules.js +3 -3
  34. package/lib/server/codegen/codegen.d.ts +20 -0
  35. package/lib/server/codegen/codegen.js +65 -0
  36. package/lib/server/codegen/codegenRoutes.d.ts +49 -0
  37. package/lib/server/codegen/codegenRoutes.js +190 -0
  38. package/lib/server/configValidation.js +6 -4
  39. package/lib/server/i18n.d.ts +2 -2
  40. package/lib/server/i18n.js +20 -2
  41. package/lib/server/plugins/actions.d.ts +19 -0
  42. package/lib/server/plugins/actions.js +62 -0
  43. package/lib/server/plugins/init.js +3 -3
  44. package/lib/server/plugins/plugins.d.ts +21 -0
  45. package/lib/server/plugins/plugins.js +188 -0
  46. package/lib/server/plugins/pluginsUtils.d.ts +16 -0
  47. package/lib/server/plugins/pluginsUtils.js +75 -0
  48. package/lib/server/plugins/routeConfig.d.ts +1 -1
  49. package/lib/server/plugins/routeConfig.js +4 -4
  50. package/lib/server/plugins/synthetic.d.ts +3 -3
  51. package/lib/server/plugins/synthetic.js +0 -2
  52. package/lib/server/routes.d.ts +3 -45
  53. package/lib/server/routes.js +16 -168
  54. package/lib/server/{index.d.ts → site.d.ts} +12 -5
  55. package/lib/server/site.js +164 -0
  56. package/lib/server/siteMetadata.d.ts +5 -4
  57. package/lib/server/siteMetadata.js +14 -10
  58. package/lib/server/translations/translations.d.ts +9 -2
  59. package/lib/server/translations/translations.js +21 -4
  60. package/lib/server/utils.d.ts +0 -2
  61. package/lib/server/utils.js +1 -9
  62. package/lib/ssg.d.ts +31 -0
  63. package/lib/ssg.js +167 -0
  64. package/lib/templates/templates.d.ts +28 -0
  65. package/lib/templates/templates.js +63 -0
  66. package/lib/utils.d.ts +9 -0
  67. package/lib/utils.js +78 -0
  68. package/lib/webpack/base.d.ts +5 -1
  69. package/lib/webpack/base.js +4 -6
  70. package/lib/webpack/client.d.ts +15 -1
  71. package/lib/webpack/client.js +78 -23
  72. package/lib/webpack/minification.d.ts +8 -0
  73. package/lib/webpack/minification.js +97 -0
  74. package/lib/webpack/server.d.ts +5 -3
  75. package/lib/webpack/server.js +41 -50
  76. package/lib/webpack/utils.d.ts +13 -4
  77. package/lib/webpack/utils.js +27 -83
  78. package/package.json +13 -11
  79. package/lib/commands/start.js +0 -212
  80. package/lib/server/index.js +0 -154
  81. package/lib/server/plugins/index.d.ts +0 -18
  82. package/lib/server/plugins/index.js +0 -106
  83. /package/lib/{webpack/templates/index.html.template.ejs → templates/dev.html.template.ejs} +0 -0
  84. /package/lib/{webpack/templates → templates}/ssr.html.template.d.ts +0 -0
  85. /package/lib/{webpack/templates → templates}/ssr.html.template.js +0 -0
@@ -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
- // Do not use logger.error here: it does not print error causes
273
- console.error(err);
274
- console.log('');
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}`;
@@ -5,4 +5,4 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { ReactNode } from 'react';
8
- export declare function renderStaticApp(app: ReactNode): Promise<string>;
8
+ export declare function renderToHtml(app: ReactNode): Promise<string>;
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { renderToPipeableStream } from 'react-dom/server';
8
8
  import { Writable } from 'stream';
9
- export async function renderStaticApp(app) {
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 { Locals } from '@slorber/static-site-generator-webpack-plugin';
8
- export default function render(locals: Locals & {
9
- path: string;
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 { minify } from 'html-minifier-terser';
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 getCompiledSSRTemplate = _.memoize((template) => eta.compile(template.trim(), {
23
- rmWhitespace: true,
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={location} context={routerContext}>
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 appHtml = await renderStaticApp(app);
77
- onLinksCollected({
78
- staticPagePath: location,
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
- const { helmet } = helmetContext;
83
- const htmlAttributes = helmet.htmlAttributes.toString();
84
- const bodyAttributes = helmet.bodyAttributes.toString();
85
- const metaStrings = [
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 (<ErrorBoundary
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
- <Head>
54
- <title>Page Error</title>
55
- </Head>
56
- <Layout>
57
- <ErrorDisplay error={error} tryAgain={tryAgain}/>
58
- </Layout>
59
- </ErrorBoundary>);
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
  }
@@ -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 LoadContextOptions } from '../server';
8
- export type BuildCLIOptions = Pick<LoadContextOptions, 'config' | 'locale' | 'outDir'> & {
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<string>;
13
+ export declare function build(siteDirParam?: string, cliOptions?: Partial<BuildCLIOptions>, forceTerminate?: boolean): Promise<void>;
@@ -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 copy_webpack_plugin_1 = tslib_1.__importDefault(require("copy-webpack-plugin"));
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 = tslib_1.__importDefault(require("../webpack/client"));
22
- const server_2 = tslib_1.__importDefault(require("../webpack/server"));
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 CleanWebpackPlugin_1 = tslib_1.__importDefault(require("../webpack/plugins/CleanWebpackPlugin"));
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, isLastLocale, }) {
44
+ async function tryToBuildLocale({ locale }) {
45
45
  try {
46
- return await buildLocale({
46
+ await utils_3.PerfLogger.async(`${logger_1.default.name(locale)}`, () => buildLocale({
47
47
  siteDir,
48
48
  locale,
49
49
  cliOptions,
50
- forceTerminate,
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 context = await (0, server_1.loadContext)({
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
- const orderedLocales = [
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
- exports.build = build;
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 props = await (0, server_1.load)({
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
- // Apply user webpack config.
103
- const { outDir, generatedFilesDir, plugins, siteConfig: { onBrokenLinks, onBrokenAnchors, staticDirectories: staticDirectoriesOption, }, routes, } = props;
104
- const clientManifestPath = path_1.default.join(generatedFilesDir, 'client-manifest.json');
105
- let clientConfig = (0, webpack_merge_1.default)(await (0, client_1.default)(props, cliOptions.minify, true), {
106
- plugins: [
107
- // Remove/clean build folders before building bundles.
108
- new CleanWebpackPlugin_1.default({ verbose: false }),
109
- // Visualize size of webpack output files with an interactive zoomable
110
- // tree map.
111
- cliOptions.bundleAnalyzer && new webpack_bundle_analyzer_1.BundleAnalyzerPlugin(),
112
- // Generate client manifests file that will be used for server bundle.
113
- new react_loadable_ssr_addon_v5_slorber_1.default({
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
- if (typeof serverConfig.output?.filename === 'string') {
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: headTags,
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
- logger_1.default.success `Generated static files in path=${path_1.default.relative(process.cwd(), outDir)}.`;
199
- if (isLastLocale) {
200
- logger_1.default.info `Use code=${'npm run serve'} command to test your build locally.`;
201
- }
202
- if (forceTerminate && isLastLocale && !cliOptions.bundleAnalyzer) {
203
- process.exit(0);
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.executePluginsConfigurePostCss)({
198
+ plugins,
199
+ config,
200
+ });
201
+ config = (0, utils_2.executePluginsConfigureWebpack)({
202
+ plugins,
203
+ config,
204
+ isServer: false,
205
+ jsLoader: props.siteConfig.webpack?.jsLoader,
206
+ });
207
+ return { clientConfig: config, clientManifestPath: result.clientManifestPath };
208
+ }
209
+ async function getBuildServerConfig({ props }) {
210
+ const { plugins } = props;
211
+ const result = await (0, server_1.default)({
212
+ props,
213
+ });
214
+ let { config } = result;
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
  }
@@ -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 LoadContextOptions } from '../server';
8
- export type DeployCLIOptions = Pick<LoadContextOptions, 'config' | 'locale' | 'outDir'> & {
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>;
@@ -14,7 +14,7 @@ const os_1 = tslib_1.__importDefault(require("os"));
14
14
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
15
  const shelljs_1 = tslib_1.__importDefault(require("shelljs"));
16
16
  const utils_1 = require("@docusaurus/utils");
17
- const server_1 = require("../server");
17
+ const site_1 = require("../server/site");
18
18
  const build_1 = require("./build");
19
19
  // GIT_PASS env variable should not appear in logs
20
20
  function obfuscateGitPass(str) {
@@ -36,7 +36,7 @@ function shellExecLog(cmd) {
36
36
  }
37
37
  async function deploy(siteDirParam = '.', cliOptions = {}) {
38
38
  const siteDir = await fs_extra_1.default.realpath(siteDirParam);
39
- const { outDir, siteConfig, siteConfigPath } = await (0, server_1.loadContext)({
39
+ const { outDir, siteConfig, siteConfigPath } = await (0, site_1.loadContext)({
40
40
  siteDir,
41
41
  config: cliOptions.config,
42
42
  outDir: cliOptions.outDir,
@@ -184,7 +184,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
184
184
  if (!cliOptions.skipBuild) {
185
185
  // Build site, then push to deploymentBranch branch of specified repo.
186
186
  try {
187
- await (0, build_1.build)(siteDir, cliOptions, false).then(runDeploy);
187
+ await (0, build_1.build)(siteDir, cliOptions, false).then(() => runDeploy(outDir));
188
188
  }
189
189
  catch (err) {
190
190
  logger_1.default.error('Deployment of the build output failed.');
@@ -9,11 +9,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.externalCommand = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
- const server_1 = require("../server");
12
+ const site_1 = require("../server/site");
13
13
  const init_1 = require("../server/plugins/init");
14
14
  async function externalCommand(cli) {
15
15
  const siteDir = await fs_extra_1.default.realpath('.');
16
- const context = await (0, server_1.loadContext)({ siteDir });
16
+ const context = await (0, site_1.loadContext)({ siteDir });
17
17
  const plugins = await (0, init_1.initPlugins)(context);
18
18
  // Plugin Lifecycle - extendCli.
19
19
  plugins.forEach((plugin) => {