@docusaurus/core 3.10.1-canary-6655 → 3.10.2

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 (37) hide show
  1. package/lib/client/BaseUrlIssueBanner/index.js +1 -1
  2. package/lib/client/exports/ComponentCreator.js +2 -0
  3. package/lib/client/exports/Link.d.ts +3 -3
  4. package/lib/client/exports/Link.js +3 -3
  5. package/lib/client/exports/isInternalUrl.js +1 -1
  6. package/lib/client/serverEntry.js +6 -1
  7. package/lib/client/serverHelmetUtils.js +1 -0
  8. package/lib/commands/build/buildLocale.d.ts +1 -1
  9. package/lib/commands/build/buildLocale.js +4 -0
  10. package/lib/commands/build/buildUtils.d.ts +1 -1
  11. package/lib/commands/cli.js +15 -3
  12. package/lib/commands/deploy.js +8 -3
  13. package/lib/commands/serve.js +3 -2
  14. package/lib/commands/start/start.d.ts +0 -3
  15. package/lib/commands/start/webpack.js +7 -4
  16. package/lib/commands/utils/listenToServer.d.ts +5 -0
  17. package/lib/commands/utils/listenToServer.js +24 -0
  18. package/lib/commands/utils/openBrowser/openBrowser.js +6 -1
  19. package/lib/server/codegen/codegenRoutes.js +1 -0
  20. package/lib/server/config.js +1 -1
  21. package/lib/server/configValidation.js +10 -1
  22. package/lib/server/getHostPort.js +1 -2
  23. package/lib/server/i18n.js +6 -2
  24. package/lib/server/plugins/plugins.js +9 -1
  25. package/lib/server/site.js +1 -1
  26. package/lib/server/siteMetadata.d.ts +1 -6
  27. package/lib/server/siteMetadata.js +12 -15
  28. package/lib/server/translations/translations.js +1 -1
  29. package/lib/ssg/ssgEnv.js +3 -2
  30. package/lib/ssg/ssgExecutor.js +5 -1
  31. package/lib/ssg/ssgGlobalResult.js +10 -1
  32. package/lib/ssg/ssgParams.d.ts +1 -0
  33. package/lib/ssg/ssgParams.js +1 -0
  34. package/lib/ssg/ssgRenderer.js +5 -1
  35. package/lib/ssg/ssgWorkerThread.js +1 -0
  36. package/lib/webpack/utils/getHttpsConfig.js +2 -2
  37. package/package.json +24 -24
@@ -53,7 +53,7 @@ function insertBanner() {
53
53
  var suggestedBaseUrl = actualHomePagePath.substr(-1) === '/'
54
54
  ? actualHomePagePath
55
55
  : actualHomePagePath + '/';
56
- suggestionContainer.innerHTML = suggestedBaseUrl;
56
+ suggestionContainer.textContent = suggestedBaseUrl;
57
57
  }
58
58
  `;
59
59
  }
@@ -94,12 +94,14 @@ export default function ComponentCreator(path, hash) {
94
94
  });
95
95
  val[keyPaths[keyPaths.length - 1]] = chunk;
96
96
  });
97
+ /* eslint-disable no-underscore-dangle */
97
98
  const Component = loadedModules.__comp;
98
99
  delete loadedModules.__comp;
99
100
  const routeContext = loadedModules.__context;
100
101
  delete loadedModules.__context;
101
102
  const routeProps = loadedModules.__props;
102
103
  delete loadedModules.__props;
104
+ /* eslint-enable no-underscore-dangle */
103
105
  // Is there any way to put this RouteContextProvider upper in the tree?
104
106
  return (<RouteContextProvider value={routeContext}>
105
107
  <Component {...loadedModules} {...routeProps} {...props}/>
@@ -4,7 +4,7 @@
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 ReactNode } from 'react';
7
+ import React from 'react';
8
8
  import type { Props } from '@docusaurus/Link';
9
- declare function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLinkCheck': noBrokenLinkCheck, autoAddBaseUrl, ...props }: Props): ReactNode;
10
- export default Link;
9
+ declare const _default: React.ForwardRefExoticComponent<Omit<Props, "ref"> & React.RefAttributes<HTMLAnchorElement>>;
10
+ export default _default;
@@ -18,14 +18,14 @@ import { useBaseUrlUtils } from './useBaseUrl';
18
18
  // this is because useBaseUrl() actually transforms relative links
19
19
  // like "introduction" to "/baseUrl/introduction" => bad behavior to fix
20
20
  const shouldAddBaseUrlAutomatically = (to) => to.startsWith('/');
21
- function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLinkCheck': noBrokenLinkCheck, autoAddBaseUrl = true, ...props }) {
21
+ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLinkCheck': noBrokenLinkCheck, autoAddBaseUrl = true, ...props }, forwardedRef) {
22
22
  const { siteConfig } = useDocusaurusContext();
23
23
  const { trailingSlash, baseUrl } = siteConfig;
24
24
  const router = siteConfig.future.experimental_router;
25
25
  const { withBaseUrl } = useBaseUrlUtils();
26
26
  const brokenLinks = useBrokenLinks();
27
27
  const innerRef = useRef(null);
28
- useImperativeHandle(props.ref, () => innerRef.current);
28
+ useImperativeHandle(forwardedRef, () => innerRef.current);
29
29
  // IMPORTANT: using to or href should not change anything
30
30
  // For example, MDX links will ALWAYS give us the href props
31
31
  // Using one prop or the other should not be used to distinguish
@@ -139,4 +139,4 @@ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLi
139
139
  // element"
140
140
  {...(isNavLink && { isActive, activeClassName })} {...testOnlyProps}/>);
141
141
  }
142
- export default Link;
142
+ export default React.forwardRef(Link);
@@ -8,7 +8,7 @@
8
8
  // Spec: https://datatracker.ietf.org/doc/html/rfc3986#section-3.1
9
9
  // In particular: scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
10
10
  export function hasProtocol(url) {
11
- return /^(?:[A-Z][A-Z\d+.-]*:|\/\/)/i.test(url);
11
+ return /^(?:[A-Za-z][A-Za-z\d+.-]*:|\/\/)/.test(url);
12
12
  }
13
13
  export default function isInternalUrl(url) {
14
14
  return typeof url !== 'undefined' && !hasProtocol(url);
@@ -13,7 +13,7 @@ import preload from './preload';
13
13
  import App from './App';
14
14
  import { createStatefulBrokenLinks, BrokenLinksProvider, } from './BrokenLinksContext';
15
15
  import { toPageCollectedMetadataInternal } from './serverHelmetUtils';
16
- const render = async ({ pathname }) => {
16
+ const render = async ({ pathname, v4RemoveLegacyPostBuildHeadAttribute, }) => {
17
17
  await preload(pathname);
18
18
  const modules = new Set();
19
19
  const routerContext = {};
@@ -33,6 +33,11 @@ const render = async ({ pathname }) => {
33
33
  const html = await renderToHtml(app);
34
34
  const { helmet } = helmetContext;
35
35
  const metadata = toPageCollectedMetadataInternal({ helmet });
36
+ // TODO Docusaurus v4 remove with deprecated postBuild({head}) API
37
+ // the returned collectedData must be serializable to run in workers
38
+ if (v4RemoveLegacyPostBuildHeadAttribute) {
39
+ metadata.helmet = null;
40
+ }
36
41
  const collectedData = {
37
42
  metadata,
38
43
  anchors: statefulBrokenLinks.getCollectedAnchors(),
@@ -23,6 +23,7 @@ export function toPageCollectedMetadataInternal({ helmet, }) {
23
23
  const tags = getBuildMetaTags(helmet);
24
24
  const noIndex = tags.some(isNoIndexTag);
25
25
  return {
26
+ helmet, // TODO Docusaurus v4 remove
26
27
  public: {
27
28
  noIndex,
28
29
  },
@@ -4,7 +4,7 @@
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 { BuildCLIOptions } from './build';
7
+ import { BuildCLIOptions } from './build';
8
8
  export type BuildLocaleParams = {
9
9
  siteDir: string;
10
10
  locale: string;
@@ -99,6 +99,9 @@ async function buildLocale({ siteDir, locale, cliOptions, }) {
99
99
  logger_1.default.success `Generated static files in path=${path_1.default.relative(process.cwd(), outDir)}.`;
100
100
  }
101
101
  async function executePluginsPostBuild({ plugins, props, collectedData, }) {
102
+ const head = props.siteConfig.future.v4.removeLegacyPostBuildHeadAttribute
103
+ ? {}
104
+ : lodash_1.default.mapValues(collectedData, (d) => d.metadata.helmet);
102
105
  const routesBuildMetadata = lodash_1.default.mapValues(collectedData, (d) => d.metadata.public);
103
106
  await Promise.all(plugins.map(async (plugin) => {
104
107
  if (!plugin.postBuild) {
@@ -106,6 +109,7 @@ async function executePluginsPostBuild({ plugins, props, collectedData, }) {
106
109
  }
107
110
  await plugin.postBuild({
108
111
  ...props,
112
+ head,
109
113
  routesBuildMetadata,
110
114
  content: plugin.content,
111
115
  });
@@ -4,7 +4,7 @@
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 { BuildCLIOptions } from './build';
7
+ import { BuildCLIOptions } from './build';
8
8
  /**
9
9
  * We disable locale path localization if CLI has a single "--locale" option
10
10
  * yarn build --locale fr => baseUrl=/ instead of baseUrl=/fr/
@@ -109,9 +109,21 @@ async function createCLIProgram({ cli, cliArgs, siteDir, config, }) {
109
109
  .option('--no-open', 'do not open page in the browser (default: false)')
110
110
  .option('--poll [interval]', 'use polling rather than watching for reload (default: false). Can specify a poll interval in milliseconds', normalizePollValue)
111
111
  .option('--no-minify', 'build website without minimizing JS bundles (default: false)')
112
- .option('--https', 'serve the dev site over HTTPS using a self-signed cert (default: false). Preferred over the HTTPS=true env var. Implied when both --ssl-cert and --ssl-key are provided.')
113
- .option('--ssl-cert <path>', 'path to a TLS certificate file (implies HTTPS). Preferred over the SSL_CRT_FILE env var; CLI takes precedence if both are set.')
114
- .option('--ssl-key <path>', 'path to a TLS private key file (implies HTTPS). Preferred over the SSL_KEY_FILE env var; CLI takes precedence if both are set.')
112
+ /*
113
+ TODO
114
+ .option(
115
+ '--https',
116
+ 'serve the dev site over HTTPS using a self-signed cert (default: false). Preferred over the HTTPS=true env var. Implied when both --ssl-cert and --ssl-key are provided.',
117
+ )
118
+ .option(
119
+ '--ssl-cert <path>',
120
+ 'path to a TLS certificate file (implies HTTPS). Preferred over the SSL_CRT_FILE env var; CLI takes precedence if both are set.',
121
+ )
122
+ .option(
123
+ '--ssl-key <path>',
124
+ 'path to a TLS private key file (implies HTTPS). Preferred over the SSL_KEY_FILE env var; CLI takes precedence if both are set.',
125
+ )
126
+ */
115
127
  .action(start_1.start);
116
128
  cli
117
129
  .command('serve [siteDir]')
@@ -203,7 +203,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
203
203
  }
204
204
  else if (commitResults.exitCode === 0) {
205
205
  // The commit might return a non-zero value when site is up to date.
206
- let websiteURL;
206
+ let websiteURL = '';
207
207
  if (githubHost === 'github.com') {
208
208
  websiteURL = projectName.includes('.github.io')
209
209
  ? `https://${organizationName}.github.io/`
@@ -213,8 +213,13 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
213
213
  // GitHub enterprise hosting.
214
214
  websiteURL = `https://${githubHost}/pages/${organizationName}/${projectName}/`;
215
215
  }
216
- exec(`echo "Website is live at ${websiteURL}."`, { failfast: true });
217
- process.exit(0);
216
+ try {
217
+ exec(`echo "Website is live at ${websiteURL}."`, { failfast: true });
218
+ process.exit(0);
219
+ }
220
+ catch (err) {
221
+ throw new Error(`Failed to execute command: ${err}`);
222
+ }
218
223
  }
219
224
  };
220
225
  if (!cliOptions.skipBuild) {
@@ -19,6 +19,7 @@ const openBrowser_1 = tslib_1.__importDefault(require("./utils/openBrowser/openB
19
19
  const config_1 = require("../server/config");
20
20
  const build_1 = require("./build/build");
21
21
  const getHostPort_1 = require("../server/getHostPort");
22
+ const listenToServer_1 = require("./utils/listenToServer");
22
23
  function redirect(res, location) {
23
24
  res.writeHead(302, {
24
25
  Location: location,
@@ -56,7 +57,7 @@ async function serve(siteDirParam = '.', cliOptions = {}) {
56
57
  if (baseUrl !== '/') {
57
58
  // Not super robust, but should be good enough for our use case
58
59
  // See https://github.com/facebook/docusaurus/pull/10090
59
- const looksLikeAsset = !!req.url.match(/\.[a-z\d]{1,4}$/i);
60
+ const looksLikeAsset = !!req.url.match(/\.[a-zA-Z\d]{1,4}$/);
60
61
  if (!looksLikeAsset) {
61
62
  const normalizedUrl = (0, utils_common_1.applyTrailingSlash)(req.url, {
62
63
  trailingSlash,
@@ -84,7 +85,7 @@ async function serve(siteDirParam = '.', cliOptions = {}) {
84
85
  });
85
86
  const url = servingUrl + baseUrl;
86
87
  logger_1.default.success `Serving path=${buildDir} directory at: url=${url}`;
87
- server.listen(port);
88
+ await (0, listenToServer_1.listenToServer)({ server, host, port });
88
89
  if (cliOptions.open && !process.env.CI) {
89
90
  await (0, openBrowser_1.default)(url);
90
91
  }
@@ -11,8 +11,5 @@ export type StartCLIOptions = HostPortOptions & Pick<LoadContextParams, 'locale'
11
11
  open?: boolean;
12
12
  poll?: boolean | number;
13
13
  minify?: boolean;
14
- https?: true;
15
- sslCert?: string;
16
- sslKey?: string;
17
14
  };
18
15
  export declare function start(siteDirParam?: string, cliOptions?: Partial<StartCLIOptions>): Promise<void>;
@@ -12,7 +12,6 @@ const path_1 = tslib_1.__importDefault(require("path"));
12
12
  const webpack_merge_1 = tslib_1.__importDefault(require("webpack-merge"));
13
13
  const bundler_1 = require("@docusaurus/bundler");
14
14
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
- // eslint-disable-next-line import/default
16
15
  const webpack_dev_server_1 = tslib_1.__importDefault(require("webpack-dev-server"));
17
16
  const evalSourceMapMiddleware_1 = tslib_1.__importDefault(require("../utils/legacy/evalSourceMapMiddleware"));
18
17
  const watcher_1 = require("./watcher");
@@ -43,9 +42,13 @@ async function createDevServerConfig({ cliOptions, props, host, port, }) {
43
42
  const { baseUrl, siteDir, siteConfig } = props;
44
43
  const pollingOptions = (0, watcher_1.createPollingOptions)(cliOptions);
45
44
  const httpsConfig = await (0, getHttpsConfig_1.default)({
46
- https: cliOptions.https,
47
- sslCert: cliOptions.sslCert,
48
- sslKey: cliOptions.sslKey,
45
+ /*
46
+ TODO Docusaurus v4: wire new CLI parameters
47
+ https: cliOptions.https,
48
+ sslCert: cliOptions.sslCert,
49
+ sslKey: cliOptions.sslKey,
50
+
51
+ */
49
52
  });
50
53
  // https://webpack.js.org/configuration/dev-server
51
54
  return {
@@ -0,0 +1,5 @@
1
+ export declare function listenToServer({ server, port, host, }: {
2
+ server: import('node:http').Server;
3
+ port: number;
4
+ host: string;
5
+ }): Promise<void>;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.listenToServer = listenToServer;
4
+ /**
5
+ * Copyright (c) Facebook, Inc. and its affiliates.
6
+ *
7
+ * This source code is licensed under the MIT license found in the
8
+ * LICENSE file in the root directory of this source tree.
9
+ */
10
+ const logger_1 = require("@docusaurus/logger");
11
+ async function listenToServer({ server, port, host, }) {
12
+ return new Promise((resolve, reject) => {
13
+ server.once('listening', () => resolve());
14
+ server.once('error', (err) => {
15
+ if (err.code === 'EADDRINUSE') {
16
+ reject(new Error(logger_1.logger.interpolate `Address in use, another server is already listening on the requested port number=${port} and host name=${host}`, { cause: err }));
17
+ }
18
+ else {
19
+ reject(err);
20
+ }
21
+ });
22
+ server.listen(port, host);
23
+ });
24
+ }
@@ -51,7 +51,12 @@ async function tryOpenWithAppleScript({ url, browser, }) {
51
51
  ];
52
52
  // Among all the supported browsers, retrieves to stdout the active ones
53
53
  const command = `ps cax -o command | grep -E "^(${supportedChromiumBrowsers.join('|')})$"`;
54
- const result = await Promise.try(() => execPromise(command)).catch(() => {
54
+ const result = await Promise
55
+ // TODO Docusaurus v4: use Promise.try()
56
+ // See why here https://github.com/facebook/docusaurus/issues/11204#issuecomment-3073480330
57
+ .resolve()
58
+ .then(() => execPromise(command))
59
+ .catch(() => {
55
60
  // Ignore all errors
56
61
  // In particular grep errors when macOS user has no Chromium-based browser open
57
62
  // See https://github.com/facebook/docusaurus/issues/11204
@@ -85,6 +85,7 @@ ${indent(parts.join(',\n'))}
85
85
  }
86
86
  const isModule = (value) => typeof value === 'string' ||
87
87
  (typeof value === 'object' &&
88
+ // eslint-disable-next-line no-underscore-dangle
88
89
  !!value?.__import);
89
90
  /**
90
91
  * Takes a {@link Module} (which is nothing more than a path plus some metadata
@@ -33,7 +33,7 @@ async function loadSiteConfig({ siteDir, customConfigFilePath, }) {
33
33
  if (!(await fs_extra_1.default.pathExists(siteConfigPath))) {
34
34
  throw new Error(`Config file at "${siteConfigPath}" not found.`);
35
35
  }
36
- const importedConfig = await (0, utils_1.loadFreshModule)(siteConfigPath, { default: true });
36
+ const importedConfig = await (0, utils_1.loadFreshModule)(siteConfigPath);
37
37
  const loadedConfig = typeof importedConfig === 'function'
38
38
  ? await importedConfig()
39
39
  : await importedConfig;
@@ -70,6 +70,7 @@ exports.DEFAULT_FASTER_CONFIG_TRUE = {
70
70
  gitEagerVcs: true,
71
71
  };
72
72
  exports.DEFAULT_FUTURE_V4_CONFIG = {
73
+ removeLegacyPostBuildHeadAttribute: false,
73
74
  useCssCascadeLayers: false,
74
75
  siteStorageNamespacing: false,
75
76
  fasterByDefault: false,
@@ -77,6 +78,7 @@ exports.DEFAULT_FUTURE_V4_CONFIG = {
77
78
  };
78
79
  // When using the "v4: true" shortcut
79
80
  exports.DEFAULT_FUTURE_V4_CONFIG_TRUE = {
81
+ removeLegacyPostBuildHeadAttribute: true,
80
82
  useCssCascadeLayers: true,
81
83
  siteStorageNamespacing: true,
82
84
  fasterByDefault: true,
@@ -225,6 +227,7 @@ const FASTER_CONFIG_SCHEMA = utils_validation_1.Joi.alternatives()
225
227
  .optional();
226
228
  const FUTURE_V4_SCHEMA = utils_validation_1.Joi.alternatives()
227
229
  .try(utils_validation_1.Joi.object({
230
+ removeLegacyPostBuildHeadAttribute: utils_validation_1.Joi.boolean().default(exports.DEFAULT_FUTURE_V4_CONFIG.removeLegacyPostBuildHeadAttribute),
228
231
  useCssCascadeLayers: utils_validation_1.Joi.boolean().default(exports.DEFAULT_FUTURE_V4_CONFIG.useCssCascadeLayers),
229
232
  siteStorageNamespacing: utils_validation_1.Joi.boolean().default(exports.DEFAULT_FUTURE_V4_CONFIG.siteStorageNamespacing),
230
233
  fasterByDefault: utils_validation_1.Joi.boolean().default(exports.DEFAULT_FUTURE_V4_CONFIG.fasterByDefault),
@@ -349,7 +352,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
349
352
  is: utils_validation_1.Joi.valid(true),
350
353
  then: utils_validation_1.Joi.optional(),
351
354
  otherwise: utils_validation_1.Joi.object()
352
- .pattern(/[\w-]+/, utils_validation_1.Joi.string())
355
+ .pattern(/[\w-]+/, utils_validation_1.Joi.alternatives().try(utils_validation_1.Joi.string(), utils_validation_1.Joi.boolean()))
353
356
  .required(),
354
357
  }),
355
358
  customElement: utils_validation_1.Joi.bool().default(false),
@@ -467,6 +470,12 @@ Please migrate and move this option to code=${'siteConfig.markdown.hooks.onBroke
467
470
  : (0, utils_1.getVcsPreset)('disabled');
468
471
  config.future.experimental_vcs = vcsConfig;
469
472
  }
473
+ if (config.future.faster.ssgWorkerThreads &&
474
+ !config.future.v4.removeLegacyPostBuildHeadAttribute) {
475
+ throw new Error(`Docusaurus config ${logger_1.default.code('future.faster.ssgWorkerThreads')} requires the future flag ${logger_1.default.code('future.v4.removeLegacyPostBuildHeadAttribute')} to be turned on.
476
+ If you use Docusaurus Faster, we recommend that you also activate Docusaurus v4 future flags: ${logger_1.default.code('{future: {v4: true}}')}
477
+ All the v4 future flags are documented here: https://docusaurus.io/docs/api/docusaurus-config#future`);
478
+ }
470
479
  if (config.future.faster.rspackPersistentCache &&
471
480
  !config.future.faster.rspackBundler) {
472
481
  throw new Error(`Docusaurus config flag ${logger_1.default.code('future.faster.rspackPersistentCache')} requires the flag ${logger_1.default.code('future.faster.rspackBundler')} to be turned on.`);
@@ -69,8 +69,7 @@ Would you like to run the app on another port instead?`),
69
69
  return shouldChangePort ? port : null;
70
70
  }
71
71
  catch (err) {
72
- logger_1.default.error `Could not find an open port at ${host}.`;
73
- throw err;
72
+ throw new Error(logger_1.default.interpolate `Could not find an open port at ${host}.`, { cause: err });
74
73
  }
75
74
  }
76
75
  async function getHostPort(options) {
@@ -23,7 +23,7 @@ function inferLanguageDisplayName(locale) {
23
23
  fallback: 'code',
24
24
  }).of(l);
25
25
  }
26
- catch {
26
+ catch (e) {
27
27
  // This is to compensate "of()" that is a bit strict
28
28
  // Looks like starting Node 22, this locale throws: "en-US-u-ca-buddhist"
29
29
  // RangeError: invalid_argument
@@ -64,7 +64,11 @@ function getDefaultDirection(localeStr) {
64
64
  const locale = new Intl.Locale(localeStr);
65
65
  // see https://github.com/tc39/proposal-intl-locale-info
66
66
  // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo
67
- return locale.getTextInfo().direction ?? 'ltr';
67
+ // Node 18.0 implements a former version of the getTextInfo() proposal
68
+ // TODO Docusaurus v4: remove the fallback to locale.textInfo
69
+ // @ts-expect-error: The TC39 proposal was updated
70
+ const textInto = locale.getTextInfo?.() ?? locale.textInfo;
71
+ return textInto.direction ?? 'ltr';
68
72
  }
69
73
  function getDefaultLocaleConfig(
70
74
  // Locale "key/identifier"
@@ -169,7 +169,15 @@ async function reloadPlugin({ pluginIdentifier, plugins: previousPlugins, contex
169
169
  plugin: previousPlugin,
170
170
  context,
171
171
  });
172
- const plugins = previousPlugins.with(previousPlugins.indexOf(previousPlugin), plugin);
172
+ /*
173
+ // TODO Docusaurus v4 - upgrade to Node 20, use array.with()
174
+ const plugins = previousPlugins.with(
175
+ previousPlugins.indexOf(previousPlugin),
176
+ plugin,
177
+ );
178
+ */
179
+ const plugins = [...previousPlugins];
180
+ plugins[previousPlugins.indexOf(previousPlugin)] = plugin;
173
181
  const allContentLoadedResult = await executeAllPluginsAllContentLoaded({
174
182
  plugins,
175
183
  context,
@@ -37,7 +37,7 @@ async function loadContext(params) {
37
37
  const { siteDir, outDir: baseOutDir = utils_1.DEFAULT_BUILD_DIR_NAME, locale, config: customConfigFilePath, automaticBaseUrlLocalizationDisabled, } = params;
38
38
  const generatedFilesDir = path_1.default.resolve(siteDir, utils_1.GENERATED_FILES_DIR_NAME);
39
39
  const { siteVersion, loadSiteConfig: { siteConfig: initialSiteConfig, siteConfigPath }, } = await (0, combine_promises_1.default)({
40
- siteVersion: (0, siteMetadata_1.tryLoadSitePackageJson)(siteDir).then((pkg) => pkg?.version),
40
+ siteVersion: (0, siteMetadata_1.loadSiteVersion)(siteDir),
41
41
  loadSiteConfig: (0, config_1.loadSiteConfig)({
42
42
  siteDir,
43
43
  customConfigFilePath,
@@ -5,14 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { LoadedPlugin, PluginVersionInformation, SiteMetadata } from '@docusaurus/types';
8
- type PackageJson = {
9
- name?: string;
10
- version?: string;
11
- };
12
- export declare function tryLoadSitePackageJson(siteDir: string): Promise<PackageJson | undefined>;
8
+ export declare function loadSiteVersion(siteDir: string): Promise<string | undefined>;
13
9
  export declare function loadPluginVersion(pluginPath: string, siteDir: string): Promise<PluginVersionInformation>;
14
10
  export declare function createSiteMetadata({ siteVersion, plugins, }: {
15
11
  siteVersion: string | undefined;
16
12
  plugins: LoadedPlugin[];
17
13
  }): SiteMetadata;
18
- export {};
@@ -6,28 +6,26 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.tryLoadSitePackageJson = tryLoadSitePackageJson;
9
+ exports.loadSiteVersion = loadSiteVersion;
10
10
  exports.loadPluginVersion = loadPluginVersion;
11
11
  exports.createSiteMetadata = createSiteMetadata;
12
12
  const tslib_1 = require("tslib");
13
13
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
14
14
  const path_1 = tslib_1.__importDefault(require("path"));
15
15
  const utils_1 = require("@docusaurus/utils");
16
- async function tryLoadPackageJson(packageJsonPath) {
16
+ async function loadPackageJsonVersion(packageJsonPath) {
17
17
  if (await fs_extra_1.default.pathExists(packageJsonPath)) {
18
- try {
19
- return (await fs_extra_1.default.readJSON(packageJsonPath));
20
- }
21
- catch (error) {
22
- throw new Error(`Couldn't load package.json file at ${packageJsonPath}`, {
23
- cause: error,
24
- });
25
- }
18
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
19
+ return require(packageJsonPath).version;
26
20
  }
27
21
  return undefined;
28
22
  }
29
- async function tryLoadSitePackageJson(siteDir) {
30
- return tryLoadPackageJson(path_1.default.join(siteDir, 'package.json'));
23
+ async function loadPackageJsonName(packageJsonPath) {
24
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
25
+ return require(packageJsonPath).name;
26
+ }
27
+ async function loadSiteVersion(siteDir) {
28
+ return loadPackageJsonVersion(path_1.default.join(siteDir, 'package.json'));
31
29
  }
32
30
  async function loadPluginVersion(pluginPath, siteDir) {
33
31
  let potentialPluginPackageJsonDirectory = path_1.default.dirname(pluginPath);
@@ -40,11 +38,10 @@ async function loadPluginVersion(pluginPath, siteDir) {
40
38
  // as local plugin.
41
39
  return { type: 'project' };
42
40
  }
43
- const packageJson = await tryLoadPackageJson(packageJsonPath);
44
41
  return {
45
42
  type: 'package',
46
- name: packageJson?.name,
47
- version: packageJson?.version,
43
+ name: await loadPackageJsonName(packageJsonPath),
44
+ version: await loadPackageJsonVersion(packageJsonPath),
48
45
  };
49
46
  }
50
47
  potentialPluginPackageJsonDirectory = path_1.default.dirname(potentialPluginPackageJsonDirectory);
@@ -62,7 +62,7 @@ function mergeTranslationFileContent({ existingContent = {}, newContent, options
62
62
  // If messages already exist, we don't override them (unless requested)
63
63
  message: options.override
64
64
  ? message
65
- : (existingContent[key]?.message ?? message),
65
+ : existingContent[key]?.message ?? message,
66
66
  description,
67
67
  };
68
68
  });
package/lib/ssg/ssgEnv.js CHANGED
@@ -9,8 +9,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.SSGWorkerThreadRecyclerMaxMemory = exports.SSGWorkerThreadTaskSize = exports.SSGWorkerThreadCount = exports.SSGConcurrency = void 0;
10
10
  // Secret way to set SSR plugin async concurrency option
11
11
  // Waiting for feedback before documenting this officially?
12
- exports.SSGConcurrency = process.env.DOCUSAURUS_SSG_CONCURRENCY
13
- ? parseInt(process.env.DOCUSAURUS_SSG_CONCURRENCY, 10)
12
+ // TODO Docusaurus v4, rename SSR => SSG
13
+ exports.SSGConcurrency = process.env.DOCUSAURUS_SSR_CONCURRENCY
14
+ ? parseInt(process.env.DOCUSAURUS_SSR_CONCURRENCY, 10)
14
15
  : // Not easy to define a reasonable option default
15
16
  // Will still be better than Infinity
16
17
  // See also https://github.com/sindresorhus/p-map/issues/24
@@ -51,7 +51,11 @@ function getNumberOfThreads(pathnames) {
51
51
  return ssgEnv_1.SSGWorkerThreadCount;
52
52
  }
53
53
  // See also https://github.com/tinylibs/tinypool/pull/108
54
- const cpuCount = os_1.default.availableParallelism();
54
+ const cpuCount =
55
+ // TODO Docusaurus v4: bump node, availableParallelism() now always exists
56
+ typeof os_1.default.availableParallelism === 'function'
57
+ ? os_1.default.availableParallelism()
58
+ : os_1.default.cpus().length;
55
59
  return inferNumberOfThreads({
56
60
  pageCount: pathnames.length,
57
61
  cpuCount,
@@ -17,11 +17,20 @@ function printSSGWarnings(results) {
17
17
  if (process.env.DOCUSAURUS_IGNORE_SSG_WARNINGS === 'true') {
18
18
  return;
19
19
  }
20
+ const ignoredWarnings = [
21
+ // TODO Docusaurus v4: remove with React 19 upgrade
22
+ // React 18 emit NULL chars, and minifier detects it
23
+ // see https://github.com/facebook/docusaurus/issues/9985
24
+ 'Unexpected null character',
25
+ ];
26
+ const keepWarning = (warning) => {
27
+ return !ignoredWarnings.some((iw) => warning.includes(iw));
28
+ };
20
29
  const resultsWithWarnings = results
21
30
  .map((success) => {
22
31
  return {
23
32
  ...success,
24
- warnings: success.result.warnings,
33
+ warnings: success.result.warnings.filter(keepWarning),
25
34
  };
26
35
  })
27
36
  .filter((result) => result.warnings.length > 0);
@@ -20,6 +20,7 @@ export type SSGParams = {
20
20
  htmlMinifierType: HtmlMinifierType;
21
21
  serverBundlePath: string;
22
22
  ssgTemplateContent: string;
23
+ v4RemoveLegacyPostBuildHeadAttribute: boolean;
23
24
  };
24
25
  export declare function createSSGParams({ props, serverBundlePath, clientManifestPath, }: {
25
26
  props: Props;
@@ -29,6 +29,7 @@ async function createSSGParams({ props, serverBundlePath, clientManifestPath, })
29
29
  htmlMinifierType: props.siteConfig.future.faster.swcHtmlMinimizer
30
30
  ? 'swc'
31
31
  : 'terser',
32
+ v4RemoveLegacyPostBuildHeadAttribute: props.siteConfig.future.v4.removeLegacyPostBuildHeadAttribute,
32
33
  };
33
34
  // Useless but ensures that SSG params remain serializable
34
35
  return structuredClone(params);
@@ -86,6 +86,7 @@ function reduceCollectedData(pageCollectedData) {
86
86
  anchors: pageCollectedData.anchors,
87
87
  metadata: {
88
88
  public: pageCollectedData.metadata.public,
89
+ helmet: pageCollectedData.metadata.helmet,
89
90
  },
90
91
  links: pageCollectedData.links,
91
92
  };
@@ -93,7 +94,10 @@ function reduceCollectedData(pageCollectedData) {
93
94
  async function generateStaticFile({ pathname, appRenderer, params, htmlMinifier, ssgTemplate, }) {
94
95
  try {
95
96
  // This only renders the app HTML
96
- const appRenderResult = await appRenderer.render({ pathname });
97
+ const appRenderResult = await appRenderer.render({
98
+ pathname,
99
+ v4RemoveLegacyPostBuildHeadAttribute: params.v4RemoveLegacyPostBuildHeadAttribute,
100
+ });
97
101
  // This renders the full page HTML, including head tags...
98
102
  const fullPageHtml = (0, ssgTemplate_1.renderSSGTemplate)({
99
103
  params,
@@ -11,6 +11,7 @@ const tslib_1 = require("tslib");
11
11
  const node_worker_threads_1 = require("node:worker_threads");
12
12
  const logger_1 = tslib_1.__importStar(require("@docusaurus/logger"));
13
13
  const ssgRenderer_js_1 = require("./ssgRenderer.js");
14
+ // eslint-disable-next-line no-underscore-dangle
14
15
  const workerId = process?.__tinypool_state__?.workerId;
15
16
  if (!workerId) {
16
17
  throw new Error('SSG Worker Thread not executing in Tinypool context?');
@@ -40,10 +40,10 @@ function validateKeyAndCerts({ cert, key }) {
40
40
  function getExplicitHttps(options) {
41
41
  return (options.https ??
42
42
  (typeof process.env.DOCUSAURUS_HTTPS !== 'undefined'
43
- ? process.env.DOCUSAURUS_HTTPS == 'true'
43
+ ? process.env.DOCUSAURUS_HTTPS === 'true'
44
44
  : undefined) ??
45
45
  (typeof process.env.HTTPS !== 'undefined'
46
- ? process.env.HTTPS == 'true'
46
+ ? process.env.HTTPS === 'true'
47
47
  : undefined));
48
48
  }
49
49
  async function readCryptoFile(filepath, source) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@docusaurus/core",
3
3
  "description": "Easy to Maintain Open Source Documentation Websites",
4
- "version": "3.10.1-canary-6655",
4
+ "version": "3.10.2",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -33,13 +33,13 @@
33
33
  "url": "https://github.com/facebook/docusaurus/issues"
34
34
  },
35
35
  "dependencies": {
36
- "@docusaurus/babel": "3.10.1-canary-6655",
37
- "@docusaurus/bundler": "3.10.1-canary-6655",
38
- "@docusaurus/logger": "3.10.1-canary-6655",
39
- "@docusaurus/mdx-loader": "3.10.1-canary-6655",
40
- "@docusaurus/utils": "3.10.1-canary-6655",
41
- "@docusaurus/utils-common": "3.10.1-canary-6655",
42
- "@docusaurus/utils-validation": "3.10.1-canary-6655",
36
+ "@docusaurus/babel": "3.10.2",
37
+ "@docusaurus/bundler": "3.10.2",
38
+ "@docusaurus/logger": "3.10.2",
39
+ "@docusaurus/mdx-loader": "3.10.2",
40
+ "@docusaurus/utils": "3.10.2",
41
+ "@docusaurus/utils-common": "3.10.2",
42
+ "@docusaurus/utils-validation": "3.10.2",
43
43
  "boxen": "^6.2.1",
44
44
  "chalk": "^4.1.2",
45
45
  "chokidar": "^3.5.3",
@@ -47,14 +47,14 @@
47
47
  "combine-promises": "^1.1.0",
48
48
  "commander": "^5.1.0",
49
49
  "core-js": "^3.31.1",
50
- "detect-port": "^1.5.1",
50
+ "detect-port": "^2.1.0",
51
51
  "escape-html": "^1.0.3",
52
52
  "eta": "^2.2.0",
53
53
  "eval": "^0.1.8",
54
54
  "execa": "^5.1.1",
55
- "fs-extra": "^11.2.0",
55
+ "fs-extra": "^11.1.1",
56
56
  "html-tags": "^3.3.1",
57
- "html-webpack-plugin": "^5.6.7",
57
+ "html-webpack-plugin": "^5.6.0",
58
58
  "leven": "^3.1.0",
59
59
  "lodash": "^4.17.21",
60
60
  "open": "^8.4.0",
@@ -66,34 +66,34 @@
66
66
  "react-router": "^5.3.4",
67
67
  "react-router-config": "^5.1.1",
68
68
  "react-router-dom": "^5.3.4",
69
- "semver": "^7.7.4",
69
+ "semver": "^7.5.4",
70
70
  "serve-handler": "^6.1.7",
71
- "tinypool": "^2.1.0",
71
+ "tinypool": "^1.0.2",
72
72
  "tslib": "^2.6.0",
73
73
  "update-notifier": "^6.0.2",
74
- "webpack": "^5.106.2",
75
- "webpack-bundle-analyzer": "^5.3.0",
76
- "webpack-dev-server": "^5.2.3",
74
+ "webpack": "^5.95.0",
75
+ "webpack-bundle-analyzer": "^4.10.2",
76
+ "webpack-dev-server": "^5.2.2",
77
77
  "webpack-merge": "^6.0.1"
78
78
  },
79
79
  "devDependencies": {
80
- "@docusaurus/module-type-aliases": "3.10.1-canary-6655",
81
- "@docusaurus/types": "3.10.1-canary-6655",
80
+ "@docusaurus/module-type-aliases": "3.10.2",
81
+ "@docusaurus/types": "3.10.2",
82
82
  "@total-typescript/shoehorn": "^0.1.2",
83
- "@types/detect-port": "^1.3.3",
84
83
  "@types/react-dom": "^19.2.3",
85
84
  "@types/react-router-config": "^5.0.7",
86
85
  "@types/serve-handler": "^6.1.4",
87
86
  "@types/update-notifier": "^6.0.4",
88
87
  "@types/webpack-bundle-analyzer": "^4.7.0",
89
88
  "@types/webpack-env": "^1.18.8",
89
+ "tmp-promise": "^3.0.3",
90
90
  "tree-node-cli": "^1.6.0"
91
91
  },
92
92
  "peerDependencies": {
93
93
  "@docusaurus/faster": "*",
94
- "@mdx-js/react": "^3.1.1",
95
- "react": "^19.2.5",
96
- "react-dom": "^19.2.5"
94
+ "@mdx-js/react": "^3.0.0",
95
+ "react": "^18.0.0 || ^19.0.0",
96
+ "react-dom": "^18.0.0 || ^19.0.0"
97
97
  },
98
98
  "peerDependenciesMeta": {
99
99
  "@docusaurus/faster": {
@@ -101,7 +101,7 @@
101
101
  }
102
102
  },
103
103
  "engines": {
104
- "node": ">=24.14"
104
+ "node": ">=20.0"
105
105
  },
106
- "gitHead": "6e93a3055178dd0af922ba627f787229dcdf948b"
106
+ "gitHead": "f37f9035584917a97a260b91fc2842cba4f8b94f"
107
107
  }