@docusaurus/core 3.10.1-canary-6655 → 3.10.1-canary-6800

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/beforeCli.mjs CHANGED
@@ -10,14 +10,14 @@
10
10
  import fs from 'fs-extra';
11
11
  import path from 'path';
12
12
  import {createRequire} from 'module';
13
- import execa from 'execa';
13
+ import {execa} from 'execa';
14
14
  import {logger} from '@docusaurus/logger';
15
15
  import semver from 'semver';
16
16
  import updateNotifier from 'update-notifier';
17
17
  import boxen from 'boxen';
18
18
  import {DOCUSAURUS_VERSION} from '@docusaurus/utils';
19
19
 
20
- const packageJson = /** @type {import("../package.json")} */ (
20
+ const packageJson = /** @type {typeof import("../package.json")} */ (
21
21
  createRequire(import.meta.url)('../package.json')
22
22
  );
23
23
  /** @type {Record<string, any>} */
@@ -111,15 +111,13 @@ export default async function beforeCli() {
111
111
  return undefined;
112
112
  }
113
113
 
114
- const yarnVersionResult = await execa.command('yarn --version');
115
- if (yarnVersionResult.exitCode === 0) {
116
- const majorVersion = parseInt(
117
- yarnVersionResult.stdout?.trim().split('.')[0] ?? '',
118
- 10,
119
- );
120
- if (!Number.isNaN(majorVersion)) {
121
- return majorVersion;
122
- }
114
+ const yarnVersionResult = await execa`yarn --version`;
115
+ const majorVersion = parseInt(
116
+ yarnVersionResult.stdout?.trim().split('.')[0] ?? '',
117
+ 10,
118
+ );
119
+ if (!Number.isNaN(majorVersion)) {
120
+ return majorVersion;
123
121
  }
124
122
 
125
123
  return undefined;
@@ -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
  }
@@ -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 React, { useEffect, useImperativeHandle, useRef, } from 'react';
7
+ import React, { useCallback, useEffect, useImperativeHandle, useRef, } from 'react';
8
8
  import { NavLink, Link as RRLink } from 'react-router-dom';
9
9
  import { applyTrailingSlash } from '@docusaurus/utils-common';
10
10
  import useDocusaurusContext from './useDocusaurusContext';
@@ -64,18 +64,23 @@ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLi
64
64
  const LinkComponent = (isNavLink ? NavLink : RRLink);
65
65
  const IOSupported = ExecutionEnvironment.canUseIntersectionObserver;
66
66
  const ioRef = useRef(undefined);
67
- const handleRef = (el) => {
67
+ const handleRef = useCallback((el) => {
68
68
  innerRef.current = el;
69
+ ioRef.current?.disconnect();
70
+ ioRef.current = undefined;
69
71
  if (IOSupported && el && isInternal) {
70
72
  // If IO supported and element reference found, set up Observer.
71
- ioRef.current = new window.IntersectionObserver((entries) => {
73
+ const observer = new window.IntersectionObserver((entries) => {
72
74
  entries.forEach((entry) => {
73
75
  if (el === entry.target) {
74
76
  // If element is in viewport, stop observing and run callback.
75
77
  // https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
76
78
  if (entry.isIntersecting || entry.intersectionRatio > 0) {
77
- ioRef.current.unobserve(el);
78
- ioRef.current.disconnect();
79
+ observer.unobserve(el);
80
+ observer.disconnect();
81
+ if (ioRef.current === observer) {
82
+ ioRef.current = undefined;
83
+ }
79
84
  if (targetLink != null) {
80
85
  window.docusaurus.prefetch(targetLink);
81
86
  }
@@ -84,9 +89,10 @@ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLi
84
89
  });
85
90
  });
86
91
  // Add element to the observer.
87
- ioRef.current.observe(el);
92
+ ioRef.current = observer;
93
+ observer.observe(el);
88
94
  }
89
- };
95
+ }, [IOSupported, isInternal, targetLink]);
90
96
  const onInteractionEnter = () => {
91
97
  if (!preloaded.current && targetLink != null) {
92
98
  window.docusaurus.preload(targetLink);
@@ -100,13 +106,7 @@ function Link({ isNavLink, to, href, activeClassName, isActive, 'data-noBrokenLi
100
106
  window.docusaurus.prefetch(targetLink);
101
107
  }
102
108
  }
103
- // When unmounting, stop intersection observer from watching.
104
- return () => {
105
- if (IOSupported && ioRef.current) {
106
- ioRef.current.disconnect();
107
- }
108
- };
109
- }, [ioRef, targetLink, IOSupported, isInternal]);
109
+ }, [targetLink, IOSupported, isInternal]);
110
110
  // It is simple local anchor link targeting current page?
111
111
  const isAnchorLink = targetLink?.startsWith('#') ?? false;
112
112
  // See also RR logic:
@@ -15,8 +15,6 @@ import { matchRoutes } from 'react-router-config';
15
15
  * @returns Promise object represents whether pathname has been preloaded
16
16
  */
17
17
  export default function preload(pathname) {
18
- const matches = Array.from(new Set([pathname, decodeURI(pathname)]))
19
- .map((p) => matchRoutes(routes, p))
20
- .flat();
18
+ const matches = Array.from(new Set([pathname, decodeURI(pathname)])).flatMap((p) => matchRoutes(routes, p));
21
19
  return Promise.all(matches.map((match) => match.route.component.preload?.()));
22
20
  }
@@ -12,7 +12,7 @@ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
12
  const path_1 = tslib_1.__importDefault(require("path"));
13
13
  const os_1 = tslib_1.__importDefault(require("os"));
14
14
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
- const execa_1 = tslib_1.__importDefault(require("execa"));
15
+ const execa_1 = require("execa");
16
16
  const utils_1 = require("@docusaurus/utils");
17
17
  const site_1 = require("../server/site");
18
18
  const build_1 = require("./build/build");
@@ -24,14 +24,12 @@ function obfuscateGitPass(str) {
24
24
  const debugMode = !!process.env.DOCUSAURUS_DEPLOY_DEBUG;
25
25
  // Log executed commands so that user can figure out mistakes on his own
26
26
  // for example: https://github.com/facebook/docusaurus/issues/3875
27
- function exec(cmd, options) {
27
+ async function exec(file, args, options) {
28
28
  const log = options?.log ?? true;
29
- const failfast = options?.failfast ?? false;
29
+ const failfast = options?.failfast ?? true;
30
+ const cmd = [file, ...args].join(' ');
30
31
  try {
31
- // TODO migrate to execa(file,[...args]) instead
32
- // Use async/await everything
33
- // Avoid execa.command: the args need to be escaped manually
34
- const result = execa_1.default.commandSync(cmd);
32
+ const result = await (0, execa_1.execa)(file, args, { reject: false });
35
33
  if (log || debugMode) {
36
34
  logger_1.default.info `code=${obfuscateGitPass(cmd)} subdue=${`code: ${result.exitCode}`}`;
37
35
  }
@@ -39,7 +37,8 @@ function exec(cmd, options) {
39
37
  console.log(result);
40
38
  }
41
39
  if (failfast && result.exitCode !== 0) {
42
- throw new Error(`Command returned unexpected exitCode ${result.exitCode}`);
40
+ throw new Error(`Command returned unexpected exitCode ${result.exitCode}
41
+ ${result.stderr}`);
43
42
  }
44
43
  return result;
45
44
  }
@@ -48,13 +47,8 @@ function exec(cmd, options) {
48
47
  In CWD code=${process.cwd()}`, { cause: err });
49
48
  }
50
49
  }
51
- // Execa escape args and add necessary quotes automatically
52
- // When using Execa.command, the args containing spaces must be escaped manually
53
- function escapeArg(arg) {
54
- return arg.replaceAll(' ', '\\ ');
55
- }
56
- function hasGit() {
57
- return exec('git --version').exitCode === 0;
50
+ async function hasGit() {
51
+ return (await exec('git', ['--version'], { failfast: false })).exitCode === 0;
58
52
  }
59
53
  async function deploy(siteDirParam = '.', cliOptions = {}) {
60
54
  const siteDir = await fs_extra_1.default.realpath(siteDirParam);
@@ -70,23 +64,16 @@ This behavior can have SEO impacts and create relative link issues.
70
64
  `);
71
65
  }
72
66
  logger_1.default.info('Deploy command invoked...');
73
- if (!hasGit()) {
67
+ if (!(await hasGit())) {
74
68
  throw new Error('Git not installed or not added to PATH!');
75
69
  }
76
70
  // Source repo is the repo from where the command is invoked
77
- const { stdout } = exec('git remote get-url origin', {
71
+ const sourceRepoUrl = (await exec('git', ['remote', 'get-url', 'origin'], {
78
72
  log: false,
79
- failfast: true,
80
- });
81
- const sourceRepoUrl = stdout.trim();
73
+ })).stdout.trim();
82
74
  // The source branch; defaults to the currently checked out branch
83
75
  const sourceBranch = process.env.CURRENT_BRANCH ??
84
- exec('git rev-parse --abbrev-ref HEAD', {
85
- log: false,
86
- failfast: true,
87
- })
88
- ?.stdout?.toString()
89
- .trim();
76
+ (await exec('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { log: false })).stdout.trim();
90
77
  const gitUser = process.env.GIT_USER;
91
78
  let useSSH = process.env.USE_SSH !== undefined &&
92
79
  process.env.USE_SSH.toLowerCase() === 'true';
@@ -116,10 +103,7 @@ This behavior can have SEO impacts and create relative link issues.
116
103
  // We never deploy on pull request.
117
104
  const isPullRequest = process.env.CI_PULL_REQUEST ?? process.env.CIRCLE_PULL_REQUEST;
118
105
  if (isPullRequest) {
119
- exec('echo "Skipping deploy on a pull request."', {
120
- log: false,
121
- failfast: true,
122
- });
106
+ logger_1.default.info('Skipping deploy on a pull request.');
123
107
  process.exit(0);
124
108
  }
125
109
  // github.io indicates organization repos that deploy via default branch. All
@@ -158,7 +142,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
158
142
  }
159
143
  // Save the commit hash that triggers publish-gh-pages before checking
160
144
  // out to deployment branch.
161
- const currentCommit = exec('git rev-parse HEAD')?.stdout?.toString().trim();
145
+ const currentCommit = (await exec('git', ['rev-parse', 'HEAD'])).stdout.trim();
162
146
  const runDeploy = async (outputDirectory) => {
163
147
  const targetDirectory = cliOptions.targetDir ?? '.';
164
148
  const fromPath = outputDirectory;
@@ -167,42 +151,46 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
167
151
  // Clones the repo into the temp folder and checks out the target branch.
168
152
  // If the branch doesn't exist, it creates a new one based on the
169
153
  // repository default branch.
170
- if (exec(`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} ${escapeArg(toPath)}`).exitCode !== 0) {
171
- exec(`git clone --depth 1 ${deploymentRepoURL} ${escapeArg(toPath)}`);
172
- exec(`git checkout -b ${deploymentBranch}`);
154
+ const cloneResult = await exec('git', [
155
+ 'clone',
156
+ '--depth',
157
+ '1',
158
+ '--branch',
159
+ deploymentBranch,
160
+ deploymentRepoURL,
161
+ toPath,
162
+ ], { failfast: false });
163
+ if (cloneResult.exitCode !== 0) {
164
+ await exec('git', ['clone', '--depth', '1', deploymentRepoURL, toPath]);
165
+ await exec('git', ['checkout', '-b', deploymentBranch]);
173
166
  }
174
167
  // Clear out any existing contents in the target directory
175
- exec(`git rm -rf ${escapeArg(targetDirectory)}`, {
176
- log: false,
177
- failfast: true,
178
- });
168
+ await exec('git', ['rm', '-rf', targetDirectory], { log: false });
179
169
  const targetPath = path_1.default.join(toPath, targetDirectory);
180
170
  try {
181
171
  await fs_extra_1.default.copy(fromPath, targetPath);
182
172
  }
183
173
  catch (err) {
184
- logger_1.default.error `Copying build assets from path=${fromPath} to path=${targetPath} failed.`;
185
- throw err;
174
+ throw new Error(`Failed to copy build assets from path=${fromPath} to path=${targetPath}.`, { cause: err });
186
175
  }
187
- exec('git add --all', { failfast: true });
176
+ await exec('git', ['add', '--all']);
188
177
  const gitUserName = process.env.GIT_USER_NAME;
189
178
  if (gitUserName) {
190
- exec(`git config user.name ${escapeArg(gitUserName)}`, { failfast: true });
179
+ await exec('git', ['config', 'user.name', gitUserName]);
191
180
  }
192
181
  const gitUserEmail = process.env.GIT_USER_EMAIL;
193
182
  if (gitUserEmail) {
194
- exec(`git config user.email ${escapeArg(gitUserEmail)}`, {
195
- failfast: true,
196
- });
183
+ await exec('git', ['config', 'user.email', gitUserEmail]);
197
184
  }
198
185
  const commitMessage = process.env.CUSTOM_COMMIT_MESSAGE ??
199
186
  `Deploy website - based on ${currentCommit}`;
200
- const commitResults = exec(`git commit -m ${escapeArg(commitMessage)} --allow-empty`);
201
- if (exec(`git push --force origin ${deploymentBranch}`).exitCode !== 0) {
187
+ // The commit might return a non-zero value when site is up to date.
188
+ const commitResults = await exec('git', ['commit', '-m', commitMessage, '--allow-empty'], { failfast: false });
189
+ const pushResult = await exec('git', ['push', '--force', 'origin', deploymentBranch], { failfast: false });
190
+ if (pushResult.exitCode !== 0) {
202
191
  throw new Error('Running "git push" command failed. Does the GitHub user account you are using have push access to the repository?');
203
192
  }
204
193
  else if (commitResults.exitCode === 0) {
205
- // The commit might return a non-zero value when site is up to date.
206
194
  let websiteURL;
207
195
  if (githubHost === 'github.com') {
208
196
  websiteURL = projectName.includes('.github.io')
@@ -213,7 +201,7 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
213
201
  // GitHub enterprise hosting.
214
202
  websiteURL = `https://${githubHost}/pages/${organizationName}/${projectName}/`;
215
203
  }
216
- exec(`echo "Website is live at ${websiteURL}."`, { failfast: true });
204
+ logger_1.default.success `Website is live at url=${websiteURL}.`;
217
205
  process.exit(0);
218
206
  }
219
207
  };
@@ -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,
@@ -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
  }
@@ -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");
@@ -139,5 +138,19 @@ async function createWebpackDevServer({ props, cliOptions, openUrlContext, }) {
139
138
  });
140
139
  // Allow plugin authors to customize/override devServer config
141
140
  const devServerConfig = (0, webpack_merge_1.default)([defaultDevServerConfig, config.devServer].filter(Boolean));
142
- return new webpack_dev_server_1.default(devServerConfig, compiler);
141
+ return createDevServer({
142
+ devServerConfig,
143
+ compiler,
144
+ currentBundler: props.currentBundler,
145
+ });
146
+ }
147
+ async function createDevServer({ devServerConfig, compiler, currentBundler, }) {
148
+ if (currentBundler.name === 'webpack') {
149
+ return new webpack_dev_server_1.default(devServerConfig, compiler);
150
+ }
151
+ else {
152
+ const RspackDevServer = await (0, bundler_1.importRspackDevServer)();
153
+ // @ts-expect-error: different types
154
+ return new RspackDevServer(devServerConfig, compiler);
155
+ }
143
156
  }
@@ -39,6 +39,11 @@ async function eject({ siteDir, themePath, componentName, typescript, }) {
39
39
  : `${fromPath}.*`;
40
40
  const globPatternPosix = (0, utils_1.posixPath)(globPattern);
41
41
  const filesToCopy = await (0, utils_1.Globby)(globPatternPosix, {
42
+ // Workaround for Tinyglobby bug?
43
+ // We glob absolute from the theme root path, not from cwd
44
+ // See https://github.com/SuperchupuDev/tinyglobby/issues/186
45
+ cwd: themePath,
46
+ absolute: true,
42
47
  ignore: lodash_1.default.compact([
43
48
  '**/*.{story,stories,test,tests}.{js,jsx,ts,tsx}',
44
49
  // When ejecting JS components, we want to avoid emitting TS files
@@ -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
+ }
@@ -15,7 +15,7 @@ const tslib_1 = require("tslib");
15
15
  /* eslint-disable */
16
16
  const child_process_1 = require("child_process");
17
17
  const util_1 = require("util");
18
- const open_1 = tslib_1.__importDefault(require("open"));
18
+ const open_1 = tslib_1.__importStar(require("open"));
19
19
  const logger_1 = require("@docusaurus/logger");
20
20
  const execPromise = (0, util_1.promisify)(child_process_1.exec);
21
21
  // Not sure if we need this, but let's keep a secret escape hatch
@@ -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
@@ -96,9 +101,9 @@ function toOpenApp(params) {
96
101
  return undefined;
97
102
  }
98
103
  // Handles "cross-platform" shortcuts like "chrome", "firefox", "edge"
99
- if (open_1.default.apps[params.browser]) {
104
+ if (open_1.apps[params.browser]) {
100
105
  return {
101
- name: open_1.default.apps[params.browser],
106
+ name: open_1.apps[params.browser],
102
107
  arguments: params.browserArgs,
103
108
  };
104
109
  }
@@ -63,9 +63,9 @@ async function writeHeadingIds(siteDirParam = '.', files = [], options = {}) {
63
63
  validateOptions(options);
64
64
  const siteDir = await fs_extra_1.default.realpath(siteDirParam);
65
65
  const patterns = files.length ? files : await getPathsToWatch(siteDir);
66
- const markdownFiles = await (0, utils_1.safeGlobby)(patterns, {
67
- expandDirectories: ['**/*.{md,mdx}'],
68
- });
66
+ const markdownFiles = (await (0, utils_1.safeGlobby)(patterns, {
67
+ expandDirectories: true,
68
+ })).filter((file) => file.endsWith('.md') || file.endsWith('.mdx'));
69
69
  if (markdownFiles.length === 0) {
70
70
  logger_1.default.warn `No markdown files found in siteDir path=${siteDir} for patterns: ${patterns}`;
71
71
  return;
@@ -16,15 +16,14 @@ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
16
16
  const DEFAULT_I18N_LOCALE = 'en';
17
17
  const SiteUrlSchema = utils_validation_1.Joi.string()
18
18
  .custom((value, helpers) => {
19
- try {
20
- const { pathname } = new URL(value);
21
- if (pathname !== '/') {
22
- return helpers.error('docusaurus.subPathError', { pathname });
23
- }
24
- }
25
- catch {
19
+ const url = URL.parse(value);
20
+ if (url === null) {
26
21
  return helpers.error('any.invalid');
27
22
  }
23
+ const { pathname } = url;
24
+ if (pathname !== '/') {
25
+ return helpers.error('docusaurus.subPathError', { pathname });
26
+ }
28
27
  return (0, utils_common_1.removeTrailingSlash)(value);
29
28
  })
30
29
  .messages({
@@ -91,6 +90,7 @@ exports.DEFAULT_FUTURE_CONFIG = {
91
90
  exports.DEFAULT_MARKDOWN_HOOKS = {
92
91
  onBrokenMarkdownLinks: 'warn',
93
92
  onBrokenMarkdownImages: 'throw',
93
+ onUnusedMarkdownDirectives: 'warn',
94
94
  };
95
95
  exports.DEFAULT_MARKDOWN_MDX1COMPAT = {
96
96
  comments: true,
@@ -349,7 +349,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
349
349
  is: utils_validation_1.Joi.valid(true),
350
350
  then: utils_validation_1.Joi.optional(),
351
351
  otherwise: utils_validation_1.Joi.object()
352
- .pattern(/[\w-]+/, utils_validation_1.Joi.string())
352
+ .pattern(/[\w-]+/, utils_validation_1.Joi.alternatives().try(utils_validation_1.Joi.string(), utils_validation_1.Joi.boolean()))
353
353
  .required(),
354
354
  }),
355
355
  customElement: utils_validation_1.Joi.bool().default(false),
@@ -412,6 +412,9 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
412
412
  onBrokenMarkdownImages: utils_validation_1.Joi.alternatives()
413
413
  .try(utils_validation_1.Joi.string().equal('ignore', 'log', 'warn', 'throw'), utils_validation_1.Joi.function())
414
414
  .default(exports.DEFAULT_CONFIG.markdown.hooks.onBrokenMarkdownImages),
415
+ onUnusedMarkdownDirectives: utils_validation_1.Joi.alternatives()
416
+ .try(utils_validation_1.Joi.string().equal('ignore', 'log', 'warn', 'throw'), utils_validation_1.Joi.function())
417
+ .default(exports.DEFAULT_CONFIG.markdown.hooks.onUnusedMarkdownDirectives),
415
418
  }).default(exports.DEFAULT_CONFIG.markdown.hooks),
416
419
  }).default({
417
420
  ...exports.DEFAULT_CONFIG.markdown,
@@ -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) {
@@ -9,8 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.loadHtmlTags = loadHtmlTags;
10
10
  const tslib_1 = require("tslib");
11
11
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
12
- const html_tags_1 = tslib_1.__importDefault(require("html-tags"));
13
- const void_1 = tslib_1.__importDefault(require("html-tags/void"));
12
+ const html_tags_1 = tslib_1.__importStar(require("html-tags"));
14
13
  const escape_html_1 = tslib_1.__importDefault(require("escape-html"));
15
14
  // TODO this should be done at config validation time, not here
16
15
  function assertIsHtmlTagObject(val) {
@@ -34,7 +33,7 @@ function hashRouterAbsoluteToRelativeTagAttribute(name, value) {
34
33
  }
35
34
  function htmlTagObjectToString({ tag, router, }) {
36
35
  assertIsHtmlTagObject(tag);
37
- const isVoidTag = void_1.default.includes(tag.tagName);
36
+ const isVoidTag = html_tags_1.voidHtmlTags.includes(tag.tagName);
38
37
  const tagAttributes = tag.attributes ?? {};
39
38
  const attributes = Object.keys(tagAttributes)
40
39
  .map((attr) => {
@@ -60,6 +60,21 @@ function getNumberOfThreads(pathnames) {
60
60
  minPagesPerCpu: 100,
61
61
  });
62
62
  }
63
+ // Workaround for Node styleText() limitation
64
+ // See https://github.com/nodejs/node/issues/65766
65
+ function getWorkerColorEnv() {
66
+ // Preserve an explicit user choice
67
+ if (process.env.FORCE_COLOR !== undefined) {
68
+ return {};
69
+ }
70
+ const depth = process.stdout.isTTY
71
+ ? (process.stdout.getColorDepth?.() ?? 0)
72
+ : 0;
73
+ if (depth > 2) {
74
+ return { FORCE_COLOR: depth >= 24 ? '3' : depth >= 8 ? '2' : '1' };
75
+ }
76
+ return {};
77
+ }
63
78
  const createPooledSSGExecutor = async ({ params, pathnames, }) => {
64
79
  const numberOfThreads = getNumberOfThreads(pathnames);
65
80
  // When the inferred or provided number of threads is just 1
@@ -80,6 +95,12 @@ const createPooledSSGExecutor = async ({ params, pathnames, }) => {
80
95
  runtime: 'worker_threads',
81
96
  isolateWorkers: false,
82
97
  workerData: { params },
98
+ env: {
99
+ // Cast is safe
100
+ // See https://github.com/tinylibs/tinypool/issues/136
101
+ ...process.env,
102
+ ...getWorkerColorEnv(),
103
+ },
83
104
  // WORKER MEMORY MANAGEMENT
84
105
  // Allows containing SSG memory leaks with a thread recycling workaround
85
106
  // See https://github.com/facebook/docusaurus/pull/11166
@@ -9,15 +9,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.compileSSGTemplate = compileSSGTemplate;
10
10
  exports.renderSSGTemplate = renderSSGTemplate;
11
11
  exports.renderHashRouterTemplate = renderHashRouterTemplate;
12
- const tslib_1 = require("tslib");
13
- const eta = tslib_1.__importStar(require("eta"));
12
+ const eta_1 = require("eta");
14
13
  const react_loadable_ssr_addon_v5_slorber_1 = require("react-loadable-ssr-addon-v5-slorber");
15
14
  const logger_1 = require("@docusaurus/logger");
16
15
  async function compileSSGTemplate(template) {
17
- const compiledTemplate = eta.compile(template.trim(), {
18
- rmWhitespace: true,
19
- });
20
- return (data) => compiledTemplate(data, eta.defaultConfig);
16
+ const eta = new eta_1.Eta({ rmWhitespace: true });
17
+ const compiledTemplate = eta.compile(template.trim());
18
+ return (data) => eta.render(compiledTemplate, data);
21
19
  }
22
20
  /**
23
21
  * Given a list of modules that were SSR an d
@@ -87,9 +87,14 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
87
87
  }
88
88
  if (props.currentBundler.name === 'rspack') {
89
89
  if (props.siteConfig.future.faster.rspackPersistentCache) {
90
- // Use cache: true + experiments.cache.type: "persistent"
91
- // See https://rspack.dev/config/experiments#persistent-cache
92
- return true;
90
+ return {
91
+ type: 'persistent',
92
+ // Rspack doesn't have "cache.name" like Webpack
93
+ // This is not ideal but work around is to merge name/version
94
+ // See https://github.com/web-infra-dev/rspack/pull/8920#issuecomment-2658938695
95
+ version: `${getCacheName()}-${getCacheVersion()}`,
96
+ buildDependencies: getCacheBuildDependencies(),
97
+ };
93
98
  }
94
99
  else {
95
100
  return disabledPersistentCacheValue;
@@ -104,29 +109,10 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
104
109
  },
105
110
  };
106
111
  }
107
- function getExperiments() {
108
- if (props.currentBundler.name === 'rspack') {
109
- // TODO find a way to type this
110
- const experiments = {};
111
- if (!process.env.DOCUSAURUS_NO_PERSISTENT_CACHE) {
112
- experiments.cache = {
113
- type: 'persistent',
114
- // Rspack doesn't have "cache.name" like Webpack
115
- // This is not ideal but work around is to merge name/version
116
- // See https://github.com/web-infra-dev/rspack/pull/8920#issuecomment-2658938695
117
- version: `${getCacheName()}-${getCacheVersion()}`,
118
- buildDependencies: getCacheBuildDependencies(),
119
- };
120
- }
121
- return experiments;
122
- }
123
- return undefined;
124
- }
125
112
  return {
126
113
  mode,
127
114
  name,
128
115
  cache: getCache(),
129
- experiments: getExperiments(),
130
116
  output: {
131
117
  pathinfo: false,
132
118
  path: outDir,
@@ -251,6 +237,10 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
251
237
  // See https://github.com/facebook/docusaurus/pull/10423
252
238
  localIdentName: `[local]_[contenthash:base64:4]`,
253
239
  exportOnlyLocals: isServer,
240
+ // Export CSS module class names compatible with css-loader v6
241
+ // export ".themedComponent--dark" instead of .themedComponentDark
242
+ // See https://github.com/webpack/css-loader/releases/tag/v7.0.0
243
+ exportLocalsConvention: 'as-is',
254
244
  },
255
245
  importLoaders: 1,
256
246
  sourceMap: !isProd,
@@ -35,7 +35,7 @@ async function createServerConfig({ props, configureWebpackUtils, }) {
35
35
  output: {
36
36
  path: outputDir,
37
37
  filename: outputFilename,
38
- libraryTarget: 'commonjs2',
38
+ library: { type: 'commonjs2' },
39
39
  },
40
40
  plugins: [
41
41
  new ProgressBarPlugin({
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.1-canary-6800",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -33,31 +33,30 @@
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.1-canary-6800",
37
+ "@docusaurus/bundler": "3.10.1-canary-6800",
38
+ "@docusaurus/logger": "3.10.1-canary-6800",
39
+ "@docusaurus/mdx-loader": "3.10.1-canary-6800",
40
+ "@docusaurus/utils": "3.10.1-canary-6800",
41
+ "@docusaurus/utils-common": "3.10.1-canary-6800",
42
+ "@docusaurus/utils-validation": "3.10.1-canary-6800",
43
43
  "boxen": "^6.2.1",
44
- "chalk": "^4.1.2",
45
44
  "chokidar": "^3.5.3",
46
45
  "cli-table3": "^0.6.3",
47
46
  "combine-promises": "^1.1.0",
48
47
  "commander": "^5.1.0",
49
48
  "core-js": "^3.31.1",
50
- "detect-port": "^1.5.1",
49
+ "detect-port": "^2.1.0",
51
50
  "escape-html": "^1.0.3",
52
- "eta": "^2.2.0",
51
+ "eta": "^4.0.1",
53
52
  "eval": "^0.1.8",
54
- "execa": "^5.1.1",
53
+ "execa": "^10.0.0",
55
54
  "fs-extra": "^11.2.0",
56
- "html-tags": "^3.3.1",
55
+ "html-tags": "^5.1.0",
57
56
  "html-webpack-plugin": "^5.6.7",
58
57
  "leven": "^3.1.0",
59
58
  "lodash": "^4.17.21",
60
- "open": "^8.4.0",
59
+ "open": "^11.0.0",
61
60
  "p-map": "^4.0.0",
62
61
  "prompts": "^2.4.2",
63
62
  "react-helmet-async": "npm:@slorber/react-helmet-async@1.3.0",
@@ -73,27 +72,29 @@
73
72
  "update-notifier": "^6.0.2",
74
73
  "webpack": "^5.106.2",
75
74
  "webpack-bundle-analyzer": "^5.3.0",
76
- "webpack-dev-server": "^5.2.3",
75
+ "webpack-dev-server": "^6.0.0",
77
76
  "webpack-merge": "^6.0.1"
78
77
  },
79
78
  "devDependencies": {
80
- "@docusaurus/module-type-aliases": "3.10.1-canary-6655",
81
- "@docusaurus/types": "3.10.1-canary-6655",
79
+ "@docusaurus/module-type-aliases": "3.10.1-canary-6800",
80
+ "@docusaurus/types": "3.10.1-canary-6800",
82
81
  "@total-typescript/shoehorn": "^0.1.2",
83
- "@types/detect-port": "^1.3.3",
84
- "@types/react-dom": "^19.2.3",
82
+ "@types/escape-html": "^1.0.4",
83
+ "@types/history": "^4.7.11",
84
+ "@types/react-dom": "^19.3.0",
85
85
  "@types/react-router-config": "^5.0.7",
86
+ "@types/react-router-dom": "^5.3.3",
86
87
  "@types/serve-handler": "^6.1.4",
87
88
  "@types/update-notifier": "^6.0.4",
88
89
  "@types/webpack-bundle-analyzer": "^4.7.0",
89
90
  "@types/webpack-env": "^1.18.8",
90
- "tree-node-cli": "^1.6.0"
91
+ "tree-node-cli": "^3.0.0"
91
92
  },
92
93
  "peerDependencies": {
93
- "@docusaurus/faster": "*",
94
+ "@docusaurus/faster": "3.10.1-canary-6800",
94
95
  "@mdx-js/react": "^3.1.1",
95
- "react": "^19.2.5",
96
- "react-dom": "^19.2.5"
96
+ "react": "^19.3.0",
97
+ "react-dom": "^19.3.0"
97
98
  },
98
99
  "peerDependenciesMeta": {
99
100
  "@docusaurus/faster": {
@@ -103,5 +104,5 @@
103
104
  "engines": {
104
105
  "node": ">=24.14"
105
106
  },
106
- "gitHead": "6e93a3055178dd0af922ba627f787229dcdf948b"
107
+ "gitHead": "7facf9bbbdb6f9f0843f143bee8a1af701bde603"
107
108
  }