@docusaurus/core 3.8.1 → 3.9.0-canary-6403

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.
@@ -44,7 +44,6 @@ export default function ComponentCreator(path, hash) {
44
44
  Object.entries(flatChunkNames).forEach(([keyPath, chunkName]) => {
45
45
  const chunkRegistry = registry[chunkName];
46
46
  if (chunkRegistry) {
47
- // eslint-disable-next-line prefer-destructuring
48
47
  loader[keyPath] = chunkRegistry[0];
49
48
  modules.push(chunkRegistry[1]);
50
49
  optsWebpack.push(chunkRegistry[2]);
@@ -14,6 +14,7 @@ const utils_1 = require("@docusaurus/utils");
14
14
  const site_1 = require("../../server/site");
15
15
  const i18n_1 = require("../../server/i18n");
16
16
  const buildLocale_1 = require("./buildLocale");
17
+ const buildUtils_1 = require("./buildUtils");
17
18
  async function build(siteDirParam = '.', cliOptions = {}) {
18
19
  process.env.BABEL_ENV = 'production';
19
20
  process.env.NODE_ENV = 'production';
@@ -49,16 +50,20 @@ function orderLocales({ locales, defaultLocale, }) {
49
50
  }
50
51
  }
51
52
  async function getLocalesToBuild({ siteDir, cliOptions, }) {
52
- // We disable locale path localization if CLI has single "--locale" option
53
- // yarn build --locale fr => baseUrl=/ instead of baseUrl=/fr/
54
- const localizePath = cliOptions.locale?.length === 1 ? false : undefined;
53
+ // TODO we shouldn't need to load all context + i18n just to get that list
54
+ // only loading siteConfig should be enough
55
55
  const context = await (0, site_1.loadContext)({
56
56
  siteDir,
57
57
  outDir: cliOptions.outDir,
58
58
  config: cliOptions.config,
59
- localizePath,
59
+ automaticBaseUrlLocalizationDisabled: (0, buildUtils_1.isAutomaticBaseUrlLocalizationDisabled)(cliOptions),
60
+ });
61
+ const i18n = await (0, i18n_1.loadI18n)({
62
+ siteDir,
63
+ config: context.siteConfig,
64
+ currentLocale: context.siteConfig.i18n.defaultLocale, // Awkward but ok
65
+ automaticBaseUrlLocalizationDisabled: false,
60
66
  });
61
- const i18n = await (0, i18n_1.loadI18n)(context.siteConfig);
62
67
  const locales = cliOptions.locale ?? i18n.locales;
63
68
  return orderLocales({
64
69
  locales: locales,
@@ -20,6 +20,7 @@ const server_1 = tslib_1.__importDefault(require("../../webpack/server"));
20
20
  const configure_1 = require("../../webpack/configure");
21
21
  const ssgExecutor_1 = require("../../ssg/ssgExecutor");
22
22
  const clearPath_1 = tslib_1.__importDefault(require("../utils/clearPath"));
23
+ const buildUtils_1 = require("./buildUtils");
23
24
  const SkipBundling = process.env.DOCUSAURUS_SKIP_BUNDLING === 'true';
24
25
  const ExitAfterLoading = process.env.DOCUSAURUS_EXIT_AFTER_LOADING === 'true';
25
26
  const ExitAfterBundling = process.env.DOCUSAURUS_EXIT_AFTER_BUNDLING === 'true';
@@ -34,7 +35,7 @@ async function buildLocale({ siteDir, locale, cliOptions, }) {
34
35
  outDir: cliOptions.outDir,
35
36
  config: cliOptions.config,
36
37
  locale,
37
- localizePath: cliOptions.locale?.length === 1 ? false : undefined,
38
+ automaticBaseUrlLocalizationDisabled: (0, buildUtils_1.isAutomaticBaseUrlLocalizationDisabled)(cliOptions),
38
39
  }));
39
40
  if (ExitAfterLoading) {
40
41
  return process.exit(0);
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+ import { BuildCLIOptions } from './build';
8
+ /**
9
+ * We disable locale path localization if CLI has a single "--locale" option
10
+ * yarn build --locale fr => baseUrl=/ instead of baseUrl=/fr/
11
+ * By default, this makes it easier to support multi-domain deployments
12
+ * See https://docusaurus.io/docs/i18n/tutorial#multi-domain-deployment
13
+ */
14
+ export declare function isAutomaticBaseUrlLocalizationDisabled(cliOptions: BuildCLIOptions): boolean;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Facebook, Inc. and its affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.isAutomaticBaseUrlLocalizationDisabled = isAutomaticBaseUrlLocalizationDisabled;
10
+ /**
11
+ * We disable locale path localization if CLI has a single "--locale" option
12
+ * yarn build --locale fr => baseUrl=/ instead of baseUrl=/fr/
13
+ * By default, this makes it easier to support multi-domain deployments
14
+ * See https://docusaurus.io/docs/i18n/tutorial#multi-domain-deployment
15
+ */
16
+ function isAutomaticBaseUrlLocalizationDisabled(cliOptions) {
17
+ return cliOptions.locale?.length === 1;
18
+ }
@@ -53,7 +53,6 @@ async function createLoadSiteParams({ siteDirParam, cliOptions, }) {
53
53
  siteDir,
54
54
  config: cliOptions.config,
55
55
  locale: cliOptions.locale,
56
- localizePath: undefined, // Should this be configurable?
57
56
  };
58
57
  }
59
58
  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
@@ -51,8 +51,14 @@ 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 execPromise(command).catch(() => {
55
- // Ignore grep errors when macOS user has no Chromium-based browser open
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(() => {
60
+ // Ignore all errors
61
+ // In particular grep errors when macOS user has no Chromium-based browser open
56
62
  // See https://github.com/facebook/docusaurus/issues/11204
57
63
  });
58
64
  if (!result) {
@@ -5,8 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { Joi } from '@docusaurus/utils-validation';
8
- import type { FasterConfig, FutureConfig, FutureV4Config, StorageConfig } from '@docusaurus/types/src/config';
9
- import type { DocusaurusConfig, I18nConfig, MarkdownConfig } from '@docusaurus/types';
8
+ import type { FasterConfig, FutureConfig, FutureV4Config, StorageConfig, DocusaurusConfig, I18nConfig, MarkdownConfig, MarkdownHooks } from '@docusaurus/types';
10
9
  export declare const DEFAULT_I18N_CONFIG: I18nConfig;
11
10
  export declare const DEFAULT_STORAGE_CONFIG: StorageConfig;
12
11
  export declare const DEFAULT_FASTER_CONFIG: FasterConfig;
@@ -14,6 +13,7 @@ export declare const DEFAULT_FASTER_CONFIG_TRUE: FasterConfig;
14
13
  export declare const DEFAULT_FUTURE_V4_CONFIG: FutureV4Config;
15
14
  export declare const DEFAULT_FUTURE_V4_CONFIG_TRUE: FutureV4Config;
16
15
  export declare const DEFAULT_FUTURE_CONFIG: FutureConfig;
16
+ export declare const DEFAULT_MARKDOWN_HOOKS: MarkdownHooks;
17
17
  export declare const DEFAULT_MARKDOWN_CONFIG: MarkdownConfig;
18
18
  export declare const DEFAULT_CONFIG: Pick<DocusaurusConfig, 'i18n' | 'future' | 'onBrokenLinks' | 'onBrokenAnchors' | 'onBrokenMarkdownLinks' | 'onDuplicateRoutes' | 'plugins' | 'themes' | 'presets' | 'headTags' | 'stylesheets' | 'scripts' | 'clientModules' | 'customFields' | 'themeConfig' | 'titleDelimiter' | 'noIndex' | 'tagline' | 'baseUrlIssueBanner' | 'staticDirectories' | 'markdown'>;
19
19
  export declare const ConfigSchema: Joi.ObjectSchema<DocusaurusConfig>;
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.ConfigSchema = exports.DEFAULT_CONFIG = exports.DEFAULT_MARKDOWN_CONFIG = exports.DEFAULT_FUTURE_CONFIG = exports.DEFAULT_FUTURE_V4_CONFIG_TRUE = exports.DEFAULT_FUTURE_V4_CONFIG = exports.DEFAULT_FASTER_CONFIG_TRUE = exports.DEFAULT_FASTER_CONFIG = exports.DEFAULT_STORAGE_CONFIG = exports.DEFAULT_I18N_CONFIG = void 0;
9
+ exports.ConfigSchema = exports.DEFAULT_CONFIG = exports.DEFAULT_MARKDOWN_CONFIG = exports.DEFAULT_MARKDOWN_HOOKS = exports.DEFAULT_FUTURE_CONFIG = exports.DEFAULT_FUTURE_V4_CONFIG_TRUE = exports.DEFAULT_FUTURE_V4_CONFIG = exports.DEFAULT_FASTER_CONFIG_TRUE = exports.DEFAULT_FASTER_CONFIG = exports.DEFAULT_STORAGE_CONFIG = exports.DEFAULT_I18N_CONFIG = void 0;
10
10
  exports.validateConfig = validateConfig;
11
11
  const tslib_1 = require("tslib");
12
12
  const utils_1 = require("@docusaurus/utils");
@@ -14,6 +14,28 @@ const utils_validation_1 = require("@docusaurus/utils-validation");
14
14
  const utils_common_1 = require("@docusaurus/utils-common");
15
15
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
16
16
  const DEFAULT_I18N_LOCALE = 'en';
17
+ const SiteUrlSchema = utils_validation_1.Joi.string()
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 {
26
+ return helpers.error('any.invalid');
27
+ }
28
+ return (0, utils_common_1.removeTrailingSlash)(value);
29
+ })
30
+ .messages({
31
+ 'any.invalid': '"{#value}" does not look like a valid URL. Make sure it has a protocol; for example, "https://example.com".',
32
+ 'docusaurus.subPathError': 'The url is not supposed to contain a sub-path like "{#pathname}". Please use the baseUrl field for sub-paths.',
33
+ });
34
+ const BaseUrlSchema = utils_validation_1.Joi
35
+ // Weird Joi trick needed, otherwise value '' is not normalized...
36
+ .alternatives()
37
+ .try(utils_validation_1.Joi.string().required().allow(''))
38
+ .custom((value) => (0, utils_common_1.addLeadingSlash)((0, utils_common_1.addTrailingSlash)(value)));
17
39
  exports.DEFAULT_I18N_CONFIG = {
18
40
  defaultLocale: DEFAULT_I18N_LOCALE,
19
41
  path: utils_1.DEFAULT_I18N_DIR_NAME,
@@ -60,9 +82,14 @@ exports.DEFAULT_FUTURE_CONFIG = {
60
82
  experimental_storage: exports.DEFAULT_STORAGE_CONFIG,
61
83
  experimental_router: 'browser',
62
84
  };
85
+ exports.DEFAULT_MARKDOWN_HOOKS = {
86
+ onBrokenMarkdownLinks: 'warn',
87
+ onBrokenMarkdownImages: 'throw',
88
+ };
63
89
  exports.DEFAULT_MARKDOWN_CONFIG = {
64
90
  format: 'mdx', // TODO change this to "detect" in Docusaurus v4?
65
91
  mermaid: false,
92
+ emoji: true,
66
93
  preprocessor: undefined,
67
94
  parseFrontMatter: utils_1.DEFAULT_PARSE_FRONT_MATTER,
68
95
  mdx1Compat: {
@@ -74,13 +101,14 @@ exports.DEFAULT_MARKDOWN_CONFIG = {
74
101
  maintainCase: false,
75
102
  },
76
103
  remarkRehypeOptions: undefined,
104
+ hooks: exports.DEFAULT_MARKDOWN_HOOKS,
77
105
  };
78
106
  exports.DEFAULT_CONFIG = {
79
107
  i18n: exports.DEFAULT_I18N_CONFIG,
80
108
  future: exports.DEFAULT_FUTURE_CONFIG,
81
109
  onBrokenLinks: 'throw',
82
110
  onBrokenAnchors: 'warn', // TODO Docusaurus v4: change to throw
83
- onBrokenMarkdownLinks: 'warn',
111
+ onBrokenMarkdownLinks: undefined,
84
112
  onDuplicateRoutes: 'warn',
85
113
  plugins: [],
86
114
  themes: [],
@@ -149,9 +177,11 @@ const PresetSchema = utils_validation_1.Joi.alternatives()
149
177
  const LocaleConfigSchema = utils_validation_1.Joi.object({
150
178
  label: utils_validation_1.Joi.string(),
151
179
  htmlLang: utils_validation_1.Joi.string(),
152
- direction: utils_validation_1.Joi.string().equal('ltr', 'rtl').default('ltr'),
180
+ direction: utils_validation_1.Joi.string().equal('ltr', 'rtl'),
153
181
  calendar: utils_validation_1.Joi.string(),
154
182
  path: utils_validation_1.Joi.string(),
183
+ url: SiteUrlSchema,
184
+ baseUrl: BaseUrlSchema,
155
185
  });
156
186
  const I18N_CONFIG_SCHEMA = utils_validation_1.Joi.object({
157
187
  defaultLocale: utils_validation_1.Joi.string().required(),
@@ -207,36 +237,13 @@ const FUTURE_CONFIG_SCHEMA = utils_validation_1.Joi.object({
207
237
  })
208
238
  .optional()
209
239
  .default(exports.DEFAULT_FUTURE_CONFIG);
210
- const SiteUrlSchema = utils_validation_1.Joi.string()
211
- .required()
212
- .custom((value, helpers) => {
213
- try {
214
- const { pathname } = new URL(value);
215
- if (pathname !== '/') {
216
- return helpers.error('docusaurus.subPathError', { pathname });
217
- }
218
- }
219
- catch {
220
- return helpers.error('any.invalid');
221
- }
222
- return (0, utils_common_1.removeTrailingSlash)(value);
223
- })
224
- .messages({
225
- 'any.invalid': '"{#value}" does not look like a valid URL. Make sure it has a protocol; for example, "https://example.com".',
226
- 'docusaurus.subPathError': 'The url is not supposed to contain a sub-path like "{#pathname}". Please use the baseUrl field for sub-paths.',
227
- });
228
240
  // TODO move to @docusaurus/utils-validation
229
241
  exports.ConfigSchema = utils_validation_1.Joi.object({
230
- baseUrl: utils_validation_1.Joi
231
- // Weird Joi trick needed, otherwise value '' is not normalized...
232
- .alternatives()
233
- .try(utils_validation_1.Joi.string().required().allow(''))
234
- .required()
235
- .custom((value) => (0, utils_common_1.addLeadingSlash)((0, utils_common_1.addTrailingSlash)(value))),
242
+ url: SiteUrlSchema.required(),
243
+ baseUrl: BaseUrlSchema.required(),
236
244
  baseUrlIssueBanner: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.baseUrlIssueBanner),
237
245
  favicon: utils_validation_1.Joi.string().optional(),
238
246
  title: utils_validation_1.Joi.string().required(),
239
- url: SiteUrlSchema,
240
247
  trailingSlash: utils_validation_1.Joi.boolean(), // No default value! undefined = retrocompatible legacy behavior!
241
248
  i18n: I18N_CONFIG_SCHEMA,
242
249
  future: FUTURE_CONFIG_SCHEMA,
@@ -248,7 +255,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
248
255
  .default(exports.DEFAULT_CONFIG.onBrokenAnchors),
249
256
  onBrokenMarkdownLinks: utils_validation_1.Joi.string()
250
257
  .equal('ignore', 'log', 'warn', 'throw')
251
- .default(exports.DEFAULT_CONFIG.onBrokenMarkdownLinks),
258
+ .default(() => exports.DEFAULT_CONFIG.onBrokenMarkdownLinks),
252
259
  onDuplicateRoutes: utils_validation_1.Joi.string()
253
260
  .equal('ignore', 'log', 'warn', 'throw')
254
261
  .default(exports.DEFAULT_CONFIG.onDuplicateRoutes),
@@ -315,6 +322,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
315
322
  .default(exports.DEFAULT_CONFIG.markdown.format),
316
323
  parseFrontMatter: utils_validation_1.Joi.function().default(() => exports.DEFAULT_CONFIG.markdown.parseFrontMatter),
317
324
  mermaid: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.markdown.mermaid),
325
+ emoji: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.markdown.emoji),
318
326
  preprocessor: utils_validation_1.Joi.function()
319
327
  .arity(1)
320
328
  .optional()
@@ -332,13 +340,29 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
332
340
  anchors: utils_validation_1.Joi.object({
333
341
  maintainCase: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.markdown.anchors.maintainCase),
334
342
  }).default(exports.DEFAULT_CONFIG.markdown.anchors),
343
+ hooks: utils_validation_1.Joi.object({
344
+ onBrokenMarkdownLinks: utils_validation_1.Joi.alternatives()
345
+ .try(utils_validation_1.Joi.string().equal('ignore', 'log', 'warn', 'throw'), utils_validation_1.Joi.function())
346
+ .default(exports.DEFAULT_CONFIG.markdown.hooks.onBrokenMarkdownLinks),
347
+ onBrokenMarkdownImages: utils_validation_1.Joi.alternatives()
348
+ .try(utils_validation_1.Joi.string().equal('ignore', 'log', 'warn', 'throw'), utils_validation_1.Joi.function())
349
+ .default(exports.DEFAULT_CONFIG.markdown.hooks.onBrokenMarkdownImages),
350
+ }).default(exports.DEFAULT_CONFIG.markdown.hooks),
335
351
  }).default(exports.DEFAULT_CONFIG.markdown),
336
352
  }).messages({
337
353
  'docusaurus.configValidationWarning': 'Docusaurus config validation warning. Field {#label}: {#warningMessage}',
338
354
  });
339
355
  // Expressing this kind of logic in Joi is a pain
340
356
  // We also want to decouple logic from Joi: easier to remove it later!
341
- function ensureDocusaurusConfigConsistency(config) {
357
+ function postProcessDocusaurusConfig(config) {
358
+ if (config.onBrokenMarkdownLinks) {
359
+ logger_1.default.warn `The code=${'siteConfig.onBrokenMarkdownLinks'} config option is deprecated and will be removed in Docusaurus v4.
360
+ Please migrate and move this option to code=${'siteConfig.markdown.hooks.onBrokenMarkdownLinks'} instead.`;
361
+ // For v3 retro compatibility we use the old attribute over the new one
362
+ config.markdown.hooks.onBrokenMarkdownLinks = config.onBrokenMarkdownLinks;
363
+ // We erase the former one to ensure we don't use it anywhere
364
+ config.onBrokenMarkdownLinks = undefined;
365
+ }
342
366
  if (config.future.experimental_faster.ssgWorkerThreads &&
343
367
  !config.future.v4.removeLegacyPostBuildHeadAttribute) {
344
368
  throw new Error(`Docusaurus config ${logger_1.default.code('future.experimental_faster.ssgWorkerThreads')} requires the future flag ${logger_1.default.code('future.v4.removeLegacyPostBuildHeadAttribute')} to be turned on.
@@ -371,6 +395,6 @@ function validateConfig(config, siteConfigPath) {
371
395
  : formattedError;
372
396
  throw new Error(formattedError);
373
397
  }
374
- ensureDocusaurusConfigConsistency(value);
398
+ postProcessDocusaurusConfig(value);
375
399
  return value;
376
400
  }
@@ -5,6 +5,10 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import type { I18n, DocusaurusConfig, I18nLocaleConfig } from '@docusaurus/types';
8
- import type { LoadContextParams } from './site';
9
- export declare function getDefaultLocaleConfig(locale: string): I18nLocaleConfig;
10
- export declare function loadI18n(config: DocusaurusConfig, options?: Pick<LoadContextParams, 'locale'>): Promise<I18n>;
8
+ export declare function getDefaultLocaleConfig(locale: string): Omit<I18nLocaleConfig, 'translate' | 'url' | 'baseUrl'>;
9
+ export declare function loadI18n({ siteDir, config, currentLocale, automaticBaseUrlLocalizationDisabled, }: {
10
+ siteDir: string;
11
+ config: DocusaurusConfig;
12
+ currentLocale: string;
13
+ automaticBaseUrlLocalizationDisabled: boolean;
14
+ }): Promise<I18n>;
@@ -9,7 +9,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.getDefaultLocaleConfig = getDefaultLocaleConfig;
10
10
  exports.loadI18n = loadI18n;
11
11
  const tslib_1 = require("tslib");
12
+ const path_1 = tslib_1.__importDefault(require("path"));
13
+ const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
12
14
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
+ const combine_promises_1 = tslib_1.__importDefault(require("combine-promises"));
16
+ const utils_1 = require("@docusaurus/utils");
13
17
  function inferLanguageDisplayName(locale) {
14
18
  const tryLocale = (l) => {
15
19
  try {
@@ -62,6 +66,7 @@ function getDefaultDirection(localeStr) {
62
66
  // see https://github.com/tc39/proposal-intl-locale-info
63
67
  // see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/getTextInfo
64
68
  // Node 18.0 implements a former version of the getTextInfo() proposal
69
+ // TODO Docusaurus v4: remove the fallback to locale.textInfo
65
70
  // @ts-expect-error: The TC39 proposal was updated
66
71
  const textInto = locale.getTextInfo?.() ?? locale.textInfo;
67
72
  return textInto.direction;
@@ -80,9 +85,8 @@ function getDefaultLocaleConfig(locale) {
80
85
  throw new Error(`Docusaurus couldn't get default locale config for ${locale}`, { cause: e });
81
86
  }
82
87
  }
83
- async function loadI18n(config, options) {
88
+ async function loadI18n({ siteDir, config, currentLocale, automaticBaseUrlLocalizationDisabled, }) {
84
89
  const { i18n: i18nConfig } = config;
85
- const currentLocale = options?.locale ?? i18nConfig.defaultLocale;
86
90
  if (!i18nConfig.locales.includes(currentLocale)) {
87
91
  logger_1.default.warn `The locale name=${currentLocale} was not found in your site configuration: Available locales are: ${i18nConfig.locales}
88
92
  Note: Docusaurus only support running one locale at a time.`;
@@ -90,13 +94,42 @@ Note: Docusaurus only support running one locale at a time.`;
90
94
  const locales = i18nConfig.locales.includes(currentLocale)
91
95
  ? i18nConfig.locales
92
96
  : i18nConfig.locales.concat(currentLocale);
93
- function getLocaleConfig(locale) {
94
- return {
97
+ async function getFullLocaleConfig(locale) {
98
+ const localeConfigInput = i18nConfig.localeConfigs[locale] ?? {};
99
+ const localeConfig = {
95
100
  ...getDefaultLocaleConfig(locale),
96
- ...i18nConfig.localeConfigs[locale],
101
+ ...localeConfigInput,
102
+ };
103
+ // By default, translations will be enabled if i18n/<locale> dir exists
104
+ async function inferTranslate() {
105
+ const localizationDir = path_1.default.resolve(siteDir, i18nConfig.path, localeConfig.path);
106
+ return fs_extra_1.default.pathExists(localizationDir);
107
+ }
108
+ function getInferredBaseUrl() {
109
+ const addLocaleSegment = locale !== i18nConfig.defaultLocale &&
110
+ !automaticBaseUrlLocalizationDisabled;
111
+ return (0, utils_1.normalizeUrl)([
112
+ '/',
113
+ config.baseUrl,
114
+ addLocaleSegment ? locale : '',
115
+ '/',
116
+ ]);
117
+ }
118
+ const translate = localeConfigInput.translate ?? (await inferTranslate());
119
+ const url = typeof localeConfigInput.url !== 'undefined'
120
+ ? localeConfigInput.url
121
+ : config.url;
122
+ const baseUrl = typeof localeConfigInput.baseUrl !== 'undefined'
123
+ ? (0, utils_1.normalizeUrl)(['/', localeConfigInput.baseUrl, '/'])
124
+ : getInferredBaseUrl();
125
+ return {
126
+ ...localeConfig,
127
+ translate,
128
+ url,
129
+ baseUrl,
97
130
  };
98
131
  }
99
- const localeConfigs = Object.fromEntries(locales.map((locale) => [locale, getLocaleConfig(locale)]));
132
+ const localeConfigs = await (0, combine_promises_1.default)(Object.fromEntries(locales.map((locale) => [locale, getFullLocaleConfig(locale)])));
100
133
  return {
101
134
  defaultLocale: i18nConfig.defaultLocale,
102
135
  locales,
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.loadPlugins = loadPlugins;
10
10
  exports.reloadPlugin = reloadPlugin;
11
11
  const logger_1 = require("@docusaurus/logger");
12
+ const utils_1 = require("@docusaurus/utils");
12
13
  const init_1 = require("./init");
13
14
  const synthetic_1 = require("./synthetic");
14
15
  const translations_1 = require("../translations/translations");
@@ -37,11 +38,16 @@ async function translatePluginContent({ plugin, content, context, }) {
37
38
  async function executePluginContentLoading({ plugin, context, }) {
38
39
  return logger_1.PerfLogger.async(`Load ${(0, pluginsUtils_1.formatPluginName)(plugin)}`, async () => {
39
40
  let content = await logger_1.PerfLogger.async('loadContent()', () => plugin.loadContent?.());
40
- content = await logger_1.PerfLogger.async('translatePluginContent()', () => translatePluginContent({
41
- plugin,
42
- content,
43
- context,
44
- }));
41
+ const shouldTranslate = (0, utils_1.getLocaleConfig)(context.i18n).translate;
42
+ if (shouldTranslate) {
43
+ content = await logger_1.PerfLogger.async('translatePluginContent()', () => translatePluginContent({
44
+ plugin,
45
+ content,
46
+ context,
47
+ }));
48
+ }
49
+ // If shouldTranslate === false, we still need the code translations
50
+ // Otherwise an unlocalized French site would show code strings in English
45
51
  const defaultCodeTranslations = (await logger_1.PerfLogger.async('getDefaultCodeTranslationMessages()', () => plugin.getDefaultCodeTranslationMessages?.())) ?? {};
46
52
  if (!plugin.contentLoaded) {
47
53
  return {
@@ -15,12 +15,19 @@ export type LoadContextParams = {
15
15
  /** Default is `i18n.defaultLocale` */
16
16
  locale?: string;
17
17
  /**
18
- * `true` means the paths will have the locale prepended; `false` means they
19
- * won't (useful for `yarn build -l zh-Hans` where the output should be
20
- * emitted into `build/` instead of `build/zh-Hans/`); `undefined` is like the
21
- * "smart" option where only non-default locale paths are localized
18
+ * By default, we try to automatically infer a localized baseUrl.
19
+ * We prepend `/<siteBaseUrl>/` with a `/<locale>/` path segment,
20
+ * except for the default locale.
21
+ *
22
+ * This option permits opting out of this baseUrl localization process.
23
+ * It is mostly useful to simplify config for multi-domain i18n deployments.
24
+ * See https://docusaurus.io/docs/i18n/tutorial#multi-domain-deployment
25
+ *
26
+ * In all cases, this process doesn't happen if an explicit localized baseUrl
27
+ * has been provided using `i18n.localeConfigs[].baseUrl`. We always use the
28
+ * provided value over the inferred one, letting you override it.
22
29
  */
23
- localizePath?: boolean;
30
+ automaticBaseUrlLocalizationDisabled?: boolean;
24
31
  };
25
32
  export type LoadSiteParams = LoadContextParams & {
26
33
  isReload?: boolean;
@@ -34,7 +34,7 @@ const siteMessages_1 = require("./siteMessages");
34
34
  * to plugin constructors.
35
35
  */
36
36
  async function loadContext(params) {
37
- const { siteDir, outDir: baseOutDir = utils_1.DEFAULT_BUILD_DIR_NAME, locale, config: customConfigFilePath, } = params;
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
40
  siteVersion: (0, siteMetadata_1.loadSiteVersion)(siteDir),
@@ -46,21 +46,23 @@ async function loadContext(params) {
46
46
  const currentBundler = await (0, bundler_1.getCurrentBundler)({
47
47
  siteConfig: initialSiteConfig,
48
48
  });
49
- const i18n = await (0, i18n_1.loadI18n)(initialSiteConfig, { locale });
50
- const baseUrl = (0, utils_1.localizePath)({
51
- path: initialSiteConfig.baseUrl,
52
- i18n,
53
- options: params,
54
- pathType: 'url',
55
- });
56
- const outDir = (0, utils_1.localizePath)({
57
- path: path_1.default.resolve(siteDir, baseOutDir),
58
- i18n,
59
- options: params,
60
- pathType: 'fs',
49
+ const i18n = await (0, i18n_1.loadI18n)({
50
+ siteDir,
51
+ config: initialSiteConfig,
52
+ currentLocale: locale ?? initialSiteConfig.i18n.defaultLocale,
53
+ automaticBaseUrlLocalizationDisabled: automaticBaseUrlLocalizationDisabled ?? false,
61
54
  });
62
- const localizationDir = path_1.default.resolve(siteDir, i18n.path, i18n.localeConfigs[i18n.currentLocale].path);
63
- const siteConfig = { ...initialSiteConfig, baseUrl };
55
+ const localeConfig = (0, utils_1.getLocaleConfig)(i18n);
56
+ // We use the baseUrl from the locale config.
57
+ // By default, it is inferred as /<siteConfig.baseUrl>/
58
+ // eventually including the /<locale>/ suffix
59
+ const baseUrl = localeConfig.baseUrl;
60
+ const outDir = path_1.default.join(path_1.default.resolve(siteDir, baseOutDir), baseUrl);
61
+ const localizationDir = path_1.default.resolve(siteDir, i18n.path, (0, utils_1.getLocaleConfig)(i18n).path);
62
+ const siteConfig = {
63
+ ...initialSiteConfig,
64
+ baseUrl,
65
+ };
64
66
  const codeTranslations = await (0, translations_1.loadSiteCodeTranslations)({ localizationDir });
65
67
  const siteStorage = (0, storage_1.createSiteStorage)(siteConfig);
66
68
  return {
@@ -20,10 +20,6 @@ const BundlerCPUProfilerPlugin_1 = require("./plugins/BundlerCPUProfilerPlugin")
20
20
  const CSS_REGEX = /\.css$/i;
21
21
  const CSS_MODULE_REGEX = /\.module\.css$/i;
22
22
  exports.clientDir = path_1.default.join(__dirname, '..', 'client');
23
- const LibrariesToTranspile = [
24
- 'copy-text-to-clipboard', // Contains optional catch binding, incompatible with recent versions of Edge
25
- ];
26
- const LibrariesToTranspileRegex = new RegExp(LibrariesToTranspile.map((libName) => `(node_modules/${libName})`).join('|'));
27
23
  function getReactAliases(siteDir) {
28
24
  // Escape hatch
29
25
  if (process.env.DOCUSAURUS_NO_REACT_ALIASES) {
@@ -43,8 +39,7 @@ function excludeJS(modulePath) {
43
39
  }
44
40
  // Don't transpile node_modules except any docusaurus npm package
45
41
  return (modulePath.includes('node_modules') &&
46
- !/docusaurus(?:(?!node_modules).)*\.jsx?$/.test(modulePath) &&
47
- !LibrariesToTranspileRegex.test(modulePath));
42
+ !/docusaurus(?:(?!node_modules).)*\.jsx?$/.test(modulePath));
48
43
  }
49
44
  async function createBaseConfig({ props, isServer, minify, faster, configureWebpackUtils, }) {
50
45
  const { outDir, siteDir, siteConfig, siteConfigPath, baseUrl, generatedFilesDir, routesPaths, siteMetadata, plugins, } = props;
@@ -111,35 +106,37 @@ async function createBaseConfig({ props, isServer, minify, faster, configureWebp
111
106
  }
112
107
  function getExperiments() {
113
108
  if (props.currentBundler.name === 'rspack') {
114
- const PersistentCacheAttributes = process.env
115
- .DOCUSAURUS_NO_PERSISTENT_CACHE
116
- ? {}
117
- : {
118
- cache: {
119
- type: 'persistent',
120
- // Rspack doesn't have "cache.name" like Webpack
121
- // This is not ideal but work around is to merge name/version
122
- // See https://github.com/web-infra-dev/rspack/pull/8920#issuecomment-2658938695
123
- version: `${getCacheName()}-${getCacheVersion()}`,
124
- buildDependencies: getCacheBuildDependencies(),
125
- },
126
- };
127
109
  // TODO find a way to type this
128
- return {
129
- // This is mostly useful in dev
130
- // See https://rspack.dev/config/experiments#experimentsincremental
131
- // Produces warnings in production builds
132
- // See https://github.com/web-infra-dev/rspack/pull/8311#issuecomment-2476014664
133
- // We use the same integration as Rspress, with ability to disable
134
- // See https://github.com/web-infra-dev/rspress/pull/1631
135
- // See https://github.com/facebook/docusaurus/issues/10646
136
- // @ts-expect-error: Rspack-only, not available in Webpack typedefs
137
- incremental: !isProd && !process.env.DISABLE_RSPACK_INCREMENTAL,
138
- // TODO re-enable later, there's an Rspack performance issue
139
- // see https://github.com/facebook/docusaurus/pull/11178
140
- parallelCodeSplitting: false,
141
- ...PersistentCacheAttributes,
142
- };
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
+ if (process.env.DISABLE_RSPACK_INCREMENTAL) {
122
+ // Enabled by default since Rspack 1.4
123
+ console.log('Rspack incremental disabled');
124
+ experiments.incremental = false;
125
+ }
126
+ // See https://rspack.rs/blog/announcing-1-5#barrel-file-optimization
127
+ if (process.env.DISABLE_RSPACK_LAZY_BARREL) {
128
+ console.log('Rspack lazyBarrel disabled');
129
+ experiments.lazyBarrel = false;
130
+ }
131
+ else {
132
+ // TODO remove after we upgrade to Rspack 1.6+
133
+ // Enabled by default for Rspack >= 1.6
134
+ experiments.lazyBarrel = true;
135
+ }
136
+ // TODO re-enable later, there's an Rspack performance issue
137
+ // see https://github.com/facebook/docusaurus/pull/11178
138
+ experiments.parallelCodeSplitting = false;
139
+ return experiments;
143
140
  }
144
141
  return undefined;
145
142
  }
@@ -15,6 +15,7 @@ const webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
15
15
  const react_loadable_ssr_addon_v5_slorber_1 = tslib_1.__importDefault(require("react-loadable-ssr-addon-v5-slorber"));
16
16
  const html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin"));
17
17
  const bundler_1 = require("@docusaurus/bundler");
18
+ const utils_1 = require("@docusaurus/utils");
18
19
  const base_1 = require("./base");
19
20
  const ChunkAssetPlugin_1 = tslib_1.__importDefault(require("./plugins/ChunkAssetPlugin"));
20
21
  const ForceTerminatePlugin_1 = tslib_1.__importDefault(require("./plugins/ForceTerminatePlugin"));
@@ -84,6 +85,7 @@ async function createStartClientConfig({ props, minify, poll, faster, configureW
84
85
  headTags,
85
86
  preBodyTags,
86
87
  postBodyTags,
88
+ lang: (0, utils_1.getLocaleConfig)(props.i18n).htmlLang,
87
89
  }),
88
90
  ],
89
91
  });
@@ -1,5 +1,5 @@
1
1
  <!DOCTYPE html>
2
- <html lang="en">
2
+ <html lang="<%= htmlWebpackPlugin.options.lang %>">
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="generator" content="Docusaurus">
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.8.1",
4
+ "version": "3.9.0-canary-6403",
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.8.1",
37
- "@docusaurus/bundler": "3.8.1",
38
- "@docusaurus/logger": "3.8.1",
39
- "@docusaurus/mdx-loader": "3.8.1",
40
- "@docusaurus/utils": "3.8.1",
41
- "@docusaurus/utils-common": "3.8.1",
42
- "@docusaurus/utils-validation": "3.8.1",
36
+ "@docusaurus/babel": "3.9.0-canary-6403",
37
+ "@docusaurus/bundler": "3.9.0-canary-6403",
38
+ "@docusaurus/logger": "3.9.0-canary-6403",
39
+ "@docusaurus/mdx-loader": "3.9.0-canary-6403",
40
+ "@docusaurus/utils": "3.9.0-canary-6403",
41
+ "@docusaurus/utils-common": "3.9.0-canary-6403",
42
+ "@docusaurus/utils-validation": "3.9.0-canary-6403",
43
43
  "boxen": "^6.2.1",
44
44
  "chalk": "^4.1.2",
45
45
  "chokidar": "^3.5.3",
@@ -73,12 +73,12 @@
73
73
  "update-notifier": "^6.0.2",
74
74
  "webpack": "^5.95.0",
75
75
  "webpack-bundle-analyzer": "^4.10.2",
76
- "webpack-dev-server": "^4.15.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.8.1",
81
- "@docusaurus/types": "3.8.1",
80
+ "@docusaurus/module-type-aliases": "3.9.0-canary-6403",
81
+ "@docusaurus/types": "3.9.0-canary-6403",
82
82
  "@total-typescript/shoehorn": "^0.1.2",
83
83
  "@types/detect-port": "^1.3.3",
84
84
  "@types/react-dom": "^18.2.7",
@@ -96,7 +96,7 @@
96
96
  "react-dom": "^18.0.0 || ^19.0.0"
97
97
  },
98
98
  "engines": {
99
- "node": ">=18.0"
99
+ "node": ">=20.0"
100
100
  },
101
- "gitHead": "fa8ae13e668fcbc0481ce10c0a734e2a5b397293"
101
+ "gitHead": "22c95f8cb5c070ce47e115be376fa9b828dd54df"
102
102
  }