@docusaurus/core 3.2.1 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -115,6 +115,10 @@ cli
115
115
  '--skip-build',
116
116
  'skip building website before deploy it (default: false)',
117
117
  )
118
+ .option(
119
+ '--target-dir <dir>',
120
+ 'path to the target directory to deploy to (default: `.`)',
121
+ )
118
122
  .action(deploy);
119
123
 
120
124
  /**
@@ -4,6 +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
+ /// <reference types="@docusaurus/module-type-aliases" />
7
8
  /// <reference types="react" />
8
9
  import '@generated/client-modules';
9
10
  export default function App(): JSX.Element;
package/lib/client/App.js CHANGED
@@ -21,9 +21,15 @@ import SiteMetadataDefaults from './SiteMetadataDefaults';
21
21
  // eslint-disable-next-line import/order
22
22
  import ErrorBoundary from '@docusaurus/ErrorBoundary';
23
23
  import HasHydratedDataAttribute from './hasHydratedDataAttribute';
24
- export default function App() {
25
- const routeElement = renderRoutes(routes);
24
+ const routesElement = renderRoutes(routes);
25
+ function AppNavigation() {
26
26
  const location = useLocation();
27
+ const normalizedLocation = normalizeLocation(location);
28
+ return (<PendingNavigation location={normalizedLocation}>
29
+ {routesElement}
30
+ </PendingNavigation>);
31
+ }
32
+ export default function App() {
27
33
  return (<ErrorBoundary>
28
34
  <DocusaurusContextProvider>
29
35
  <BrowserContextProvider>
@@ -31,9 +37,7 @@ export default function App() {
31
37
  <SiteMetadataDefaults />
32
38
  <SiteMetadata />
33
39
  <BaseUrlIssueBanner />
34
- <PendingNavigation location={normalizeLocation(location)}>
35
- {routeElement}
36
- </PendingNavigation>
40
+ <AppNavigation />
37
41
  </Root>
38
42
  <HasHydratedDataAttribute />
39
43
  </BrowserContextProvider>
@@ -100,10 +100,12 @@ export default function ComponentCreator(path, hash) {
100
100
  delete loadedModules.__comp;
101
101
  const routeContext = loadedModules.__context;
102
102
  delete loadedModules.__context;
103
+ const routeProps = loadedModules.__props;
104
+ delete loadedModules.__props;
103
105
  /* eslint-enable no-underscore-dangle */
104
106
  // Is there any way to put this RouteContextProvider upper in the tree?
105
107
  return (<RouteContextProvider value={routeContext}>
106
- <Component {...loadedModules} {...props}/>
108
+ <Component {...loadedModules} {...routeProps} {...props}/>
107
109
  </RouteContextProvider>);
108
110
  },
109
111
  });
@@ -7,5 +7,6 @@
7
7
  import { type LoadContextParams } from '../server/site';
8
8
  export type DeployCLIOptions = Pick<LoadContextParams, 'config' | 'locale' | 'outDir'> & {
9
9
  skipBuild?: boolean;
10
+ targetDir?: string;
10
11
  };
11
12
  export declare function deploy(siteDirParam?: string, cliOptions?: Partial<DeployCLIOptions>): Promise<void>;
@@ -128,26 +128,25 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
128
128
  // out to deployment branch.
129
129
  const currentCommit = shellExecLog('git rev-parse HEAD').stdout.trim();
130
130
  const runDeploy = async (outputDirectory) => {
131
+ const targetDirectory = cliOptions.targetDir ?? '.';
131
132
  const fromPath = outputDirectory;
132
133
  const toPath = await fs_extra_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), `${projectName}-${deploymentBranch}`));
133
134
  shelljs_1.default.cd(toPath);
134
- // Check out deployment branch when cloning repository, and then remove all
135
- // the files in the directory. If the 'clone' command fails, assume that
136
- // the deployment branch doesn't exist, and initialize git in an empty
137
- // directory, check out a clean deployment branch and add remote.
138
- if (shellExecLog(`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} "${toPath}"`).code === 0) {
139
- shellExecLog('git rm -rf .');
140
- }
141
- else {
142
- shellExecLog('git init');
135
+ // Clones the repo into the temp folder and checks out the target branch.
136
+ // If the branch doesn't exist, it creates a new one based on the
137
+ // repository default branch.
138
+ if (shellExecLog(`git clone --depth 1 --branch ${deploymentBranch} ${deploymentRepoURL} "${toPath}"`).code !== 0) {
139
+ shellExecLog(`git clone --depth 1 ${deploymentRepoURL} "${toPath}"`);
143
140
  shellExecLog(`git checkout -b ${deploymentBranch}`);
144
- shellExecLog(`git remote add origin ${deploymentRepoURL}`);
145
141
  }
142
+ // Clear out any existing contents in the target directory
143
+ shellExecLog(`git rm -rf ${targetDirectory}`);
144
+ const targetPath = path_1.default.join(toPath, targetDirectory);
146
145
  try {
147
- await fs_extra_1.default.copy(fromPath, toPath);
146
+ await fs_extra_1.default.copy(fromPath, targetPath);
148
147
  }
149
148
  catch (err) {
150
- logger_1.default.error `Copying build assets from path=${fromPath} to path=${toPath} failed.`;
149
+ logger_1.default.error `Copying build assets from path=${fromPath} to path=${targetPath} failed.`;
151
150
  throw err;
152
151
  }
153
152
  shellExecLog('git add --all');
@@ -184,7 +183,8 @@ You can also set the deploymentBranch property in docusaurus.config.js .`);
184
183
  if (!cliOptions.skipBuild) {
185
184
  // Build site, then push to deploymentBranch branch of specified repo.
186
185
  try {
187
- await (0, build_1.build)(siteDir, cliOptions, false).then(() => runDeploy(outDir));
186
+ await (0, build_1.build)(siteDir, cliOptions, false);
187
+ await runDeploy(outDir);
188
188
  }
189
189
  catch (err) {
190
190
  logger_1.default.error('Deployment of the build output failed.');
@@ -15,9 +15,16 @@ const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
15
15
  const utils_1 = require("@docusaurus/utils");
16
16
  const serve_handler_1 = tslib_1.__importDefault(require("serve-handler"));
17
17
  const openBrowser_1 = tslib_1.__importDefault(require("react-dev-utils/openBrowser"));
18
+ const utils_common_1 = require("@docusaurus/utils-common");
18
19
  const config_1 = require("../server/config");
19
20
  const build_1 = require("./build");
20
21
  const getHostPort_1 = require("../server/getHostPort");
22
+ function redirect(res, location) {
23
+ res.writeHead(302, {
24
+ Location: location,
25
+ });
26
+ res.end();
27
+ }
21
28
  async function serve(siteDirParam = '.', cliOptions = {}) {
22
29
  const siteDir = await fs_extra_1.default.realpath(siteDirParam);
23
30
  const buildDir = cliOptions.dir ?? utils_1.DEFAULT_BUILD_DIR_NAME;
@@ -40,14 +47,22 @@ async function serve(siteDirParam = '.', cliOptions = {}) {
40
47
  const server = http_1.default.createServer((req, res) => {
41
48
  // Automatically redirect requests to /baseUrl/
42
49
  if (!req.url?.startsWith(baseUrl)) {
43
- res.writeHead(302, {
44
- Location: baseUrl,
45
- });
46
- res.end();
50
+ redirect(res, baseUrl);
51
+ return;
52
+ }
53
+ // We do the redirect ourselves for a good reason
54
+ // server-handler is annoying and won't include /baseUrl/ in redirects
55
+ const normalizedUrl = (0, utils_common_1.applyTrailingSlash)(req.url, { trailingSlash, baseUrl });
56
+ if (req.url !== normalizedUrl) {
57
+ redirect(res, normalizedUrl);
47
58
  return;
48
59
  }
49
60
  // Remove baseUrl before calling serveHandler, because /baseUrl/ should
50
61
  // serve /build/index.html, not /build/baseUrl/index.html (does not exist)
62
+ // Note server-handler is really annoying here:
63
+ // - no easy way to do rewrites such as "/baseUrl/:path" => "/:path"
64
+ // - no easy way to "reapply" the baseUrl to the redirect "Location" header
65
+ // See also https://github.com/facebook/docusaurus/pull/10090
51
66
  req.url = req.url.replace(baseUrl, '/');
52
67
  (0, serve_handler_1.default)(req, res, {
53
68
  cleanUrls: true,
@@ -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 { CodeTranslations, DocusaurusConfig, GlobalData, I18n, RouteConfig, SiteMetadata } from '@docusaurus/types';
7
+ import type { CodeTranslations, DocusaurusConfig, GlobalData, I18n, PluginRouteConfig, SiteMetadata } from '@docusaurus/types';
8
8
  type CodegenParams = {
9
9
  generatedFilesDir: string;
10
10
  siteConfig: DocusaurusConfig;
@@ -14,7 +14,7 @@ type CodegenParams = {
14
14
  i18n: I18n;
15
15
  codeTranslations: CodeTranslations;
16
16
  siteMetadata: SiteMetadata;
17
- routes: RouteConfig[];
17
+ routes: PluginRouteConfig[];
18
18
  };
19
19
  export declare function generateSiteFiles(params: CodegenParams): Promise<void>;
20
20
  export {};
@@ -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 { RouteConfig, RouteChunkNames } from '@docusaurus/types';
7
+ import type { RouteConfig, RouteChunkNames, PluginRouteConfig } from '@docusaurus/types';
8
8
  type RoutesCode = {
9
9
  /** Serialized routes config that can be directly emitted into temp file. */
10
10
  routesConfig: string;
@@ -42,8 +42,8 @@ export declare function genChunkName(modulePath: string, prefix?: string, prefer
42
42
  export declare function generateRoutesCode(routeConfigs: RouteConfig[]): RoutesCode;
43
43
  type GenerateRouteFilesParams = {
44
44
  generatedFilesDir: string;
45
- routes: RouteConfig[];
45
+ routes: PluginRouteConfig[];
46
46
  baseUrl: string;
47
47
  };
48
- export declare function generateRouteFiles({ generatedFilesDir, routes, }: GenerateRouteFilesParams): Promise<void>;
48
+ export declare function generateRouteFiles({ generatedFilesDir, routes: initialRoutes, }: GenerateRouteFilesParams): Promise<void>;
49
49
  export {};
@@ -9,6 +9,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.generateRouteFiles = exports.generateRoutesCode = exports.genChunkName = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const querystring_1 = tslib_1.__importDefault(require("querystring"));
12
+ const path_1 = tslib_1.__importDefault(require("path"));
12
13
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
13
14
  const utils_1 = require("@docusaurus/utils");
14
15
  /** Indents every line of `str` by one level. */
@@ -58,7 +59,7 @@ exports.genChunkName = genChunkName;
58
59
  * is the same as react-router's `RouteConfig`. Formatting is similar to
59
60
  * `JSON.stringify` but without all the quotes.
60
61
  */
61
- function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeStrings, props, }) {
62
+ function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeStrings, attributes, }) {
62
63
  const parts = [
63
64
  `path: '${routePath}'`,
64
65
  `component: ComponentCreator('${routePath}', '${routeHash}')`,
@@ -71,10 +72,10 @@ function serializeRouteConfig({ routePath, routeHash, exact, subroutesCodeString
71
72
  ${indent(subroutesCodeStrings.join(',\n'))}
72
73
  ]`);
73
74
  }
74
- Object.entries(props).forEach(([propName, propValue]) => {
75
- const isIdentifier = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(propName);
76
- const key = isIdentifier ? propName : JSON.stringify(propName);
77
- parts.push(`${key}: ${JSON.stringify(propValue)}`);
75
+ Object.entries(attributes).forEach(([attrName, attrValue]) => {
76
+ const isIdentifier = /^[$_\p{ID_Start}][$\u200c\u200d\p{ID_Continue}]*$/u.test(attrName);
77
+ const key = isIdentifier ? attrName : JSON.stringify(attrName);
78
+ parts.push(`${key}: ${JSON.stringify(attrValue)}`);
78
79
  });
79
80
  return `{
80
81
  ${indent(parts.join(',\n'))}
@@ -114,7 +115,7 @@ function genChunkNames(routeModule, prefix, name, res) {
114
115
  * `routesPaths`, and `routesChunkNames` accordingly.
115
116
  */
116
117
  function genRouteCode(routeConfig, res) {
117
- const { path: routePath, component, modules = {}, context, routes: subroutes, priority, exact, metadata, ...props } = routeConfig;
118
+ const { path: routePath, component, modules = {}, context, routes: subroutes, priority, exact, metadata, props, plugin, ...attributes } = routeConfig;
118
119
  if (typeof routePath !== 'string' || !component) {
119
120
  throw new Error(`Invalid route config: path must be a string and component is required.
120
121
  ${JSON.stringify(routeConfig)}`);
@@ -132,7 +133,7 @@ ${JSON.stringify(routeConfig)}`);
132
133
  routeHash,
133
134
  subroutesCodeStrings: subroutes?.map((r) => genRouteCode(r, res)),
134
135
  exact,
135
- props,
136
+ attributes,
136
137
  });
137
138
  }
138
139
  /**
@@ -179,7 +180,75 @@ ${Object.entries(registry)
179
180
  `);
180
181
  const genRoutesChunkNames = ({ generatedFilesDir, routesChunkNames, }) => (0, utils_1.generate)(generatedFilesDir, 'routesChunkNames.json', JSON.stringify(routesChunkNames, null, 2));
181
182
  const genRoutes = ({ generatedFilesDir, routesConfig, }) => (0, utils_1.generate)(generatedFilesDir, 'routes.js', routesConfig);
182
- async function generateRouteFiles({ generatedFilesDir, routes, }) {
183
+ async function generateRoutePropModule({ generatedFilesDir, route, plugin, }) {
184
+ ensureNoPropsConflict(route);
185
+ const moduleContent = JSON.stringify(route.props);
186
+ // TODO we should aim to reduce this path length
187
+ // This adds bytes to the global module registry
188
+ const relativePath = path_1.default.posix.join(plugin.name, plugin.id, 'p', `${(0, utils_1.docuHash)(route.path)}.json`);
189
+ const modulePath = path_1.default.posix.join(generatedFilesDir, relativePath);
190
+ const aliasedPath = path_1.default.posix.join('@generated', relativePath);
191
+ await (0, utils_1.generate)(generatedFilesDir, modulePath, moduleContent);
192
+ return aliasedPath;
193
+ }
194
+ function ensureNoPropsConflict(route) {
195
+ if (!route.props && !route.modules) {
196
+ return;
197
+ }
198
+ const conflictingPropNames = lodash_1.default.intersection(Object.keys(route.props ?? {}), Object.keys(route.modules ?? {}));
199
+ if (conflictingPropNames.length > 0) {
200
+ throw new Error(`Route ${route.path} has conflicting props declared using both route.modules and route.props APIs for keys: ${conflictingPropNames.join(', ')}\nThis is not permitted, otherwise one prop would override the over.`);
201
+ }
202
+ }
203
+ async function preprocessRouteProps({ generatedFilesDir, route, plugin, }) {
204
+ const propsModulePathPromise = route.props
205
+ ? generateRoutePropModule({
206
+ generatedFilesDir,
207
+ route,
208
+ plugin,
209
+ })
210
+ : undefined;
211
+ const subRoutesPromise = route.routes
212
+ ? Promise.all(route.routes.map((subRoute) => {
213
+ return preprocessRouteProps({
214
+ generatedFilesDir,
215
+ route: subRoute,
216
+ plugin,
217
+ });
218
+ }))
219
+ : undefined;
220
+ const [propsModulePath, subRoutes] = await Promise.all([
221
+ propsModulePathPromise,
222
+ subRoutesPromise,
223
+ ]);
224
+ const newRoute = {
225
+ ...route,
226
+ modules: {
227
+ ...route.modules,
228
+ ...(propsModulePath && { __props: propsModulePath }),
229
+ },
230
+ routes: subRoutes,
231
+ props: undefined,
232
+ };
233
+ return newRoute;
234
+ }
235
+ // For convenience, it's possible to pass a "route.props" object
236
+ // This method converts the props object to a regular module
237
+ // and assigns it to route.modules.__props attribute
238
+ async function preprocessAllPluginsRoutesProps({ generatedFilesDir, routes, }) {
239
+ return Promise.all(routes.map((route) => {
240
+ return preprocessRouteProps({
241
+ generatedFilesDir,
242
+ route,
243
+ plugin: route.plugin,
244
+ });
245
+ }));
246
+ }
247
+ async function generateRouteFiles({ generatedFilesDir, routes: initialRoutes, }) {
248
+ const routes = await preprocessAllPluginsRoutesProps({
249
+ generatedFilesDir,
250
+ routes: initialRoutes,
251
+ });
183
252
  const { registry, routesChunkNames, routesConfig } = generateRoutesCode(routes);
184
253
  await Promise.all([
185
254
  genRegistry({ generatedFilesDir, registry }),
@@ -18,7 +18,7 @@ exports.DEFAULT_I18N_CONFIG = {
18
18
  localeConfigs: {},
19
19
  };
20
20
  exports.DEFAULT_MARKDOWN_CONFIG = {
21
- format: 'mdx',
21
+ format: 'mdx', // TODO change this to "detect" in Docusaurus v4?
22
22
  mermaid: false,
23
23
  preprocessor: undefined,
24
24
  parseFrontMatter: utils_1.DEFAULT_PARSE_FRONT_MATTER,
@@ -27,12 +27,15 @@ exports.DEFAULT_MARKDOWN_CONFIG = {
27
27
  admonitions: true,
28
28
  headingIds: true,
29
29
  },
30
+ anchors: {
31
+ maintainCase: false,
32
+ },
30
33
  remarkRehypeOptions: undefined,
31
34
  };
32
35
  exports.DEFAULT_CONFIG = {
33
36
  i18n: exports.DEFAULT_I18N_CONFIG,
34
37
  onBrokenLinks: 'throw',
35
- onBrokenAnchors: 'warn',
38
+ onBrokenAnchors: 'warn', // TODO Docusaurus v4: change to throw
36
39
  onBrokenMarkdownLinks: 'warn',
37
40
  onDuplicateRoutes: 'warn',
38
41
  plugins: [],
@@ -147,7 +150,7 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
147
150
  favicon: utils_validation_1.Joi.string().optional(),
148
151
  title: utils_validation_1.Joi.string().required(),
149
152
  url: SiteUrlSchema,
150
- trailingSlash: utils_validation_1.Joi.boolean(),
153
+ trailingSlash: utils_validation_1.Joi.boolean(), // No default value! undefined = retrocompatible legacy behavior!
151
154
  i18n: I18N_CONFIG_SCHEMA,
152
155
  onBrokenLinks: utils_validation_1.Joi.string()
153
156
  .equal('ignore', 'log', 'warn', 'throw')
@@ -238,6 +241,9 @@ exports.ConfigSchema = utils_validation_1.Joi.object({
238
241
  // Not sure if it's a good idea, validation is likely to become stale
239
242
  // See https://github.com/remarkjs/remark-rehype#options
240
243
  utils_validation_1.Joi.object().unknown(),
244
+ anchors: utils_validation_1.Joi.object({
245
+ maintainCase: utils_validation_1.Joi.boolean().default(exports.DEFAULT_CONFIG.markdown.anchors.maintainCase),
246
+ }).default(exports.DEFAULT_CONFIG.markdown.anchors),
241
247
  }).default(exports.DEFAULT_CONFIG.markdown),
242
248
  }).messages({
243
249
  'docusaurus.configValidationWarning': 'Docusaurus config validation warning. Field {#label}: {#warningMessage}',
@@ -18,13 +18,20 @@ const routeConfig_1 = require("./routeConfig");
18
18
  async function createPluginActionsUtils({ plugin, generatedFilesDir, baseUrl, trailingSlash, }) {
19
19
  const pluginId = plugin.options.id;
20
20
  // Plugins data files are namespaced by pluginName/pluginId
21
+ // TODO Docusaurus v4 breaking change
22
+ // module aliasing should be automatic
23
+ // we should never find local absolute FS paths in the codegen registry
24
+ const aliasedSource = (source) => `@generated/${(0, utils_1.posixPath)(path_1.default.relative(generatedFilesDir, source))}`;
25
+ // TODO use @generated data dir here!
26
+ // The module registry should not contain absolute paths
21
27
  const dataDir = path_1.default.join(generatedFilesDir, plugin.name, pluginId);
22
28
  const pluginRouteContext = {
23
29
  name: plugin.name,
24
30
  id: pluginId,
25
31
  };
26
- const pluginRouteContextModulePath = path_1.default.join(dataDir, `${(0, utils_1.docuHash)('pluginRouteContextModule')}.json`);
32
+ const pluginRouteContextModulePath = path_1.default.join(dataDir, `__plugin.json`);
27
33
  // TODO not ideal place to generate that file
34
+ // move to codegen step instead!
28
35
  await (0, utils_1.generate)('/', pluginRouteContextModulePath, JSON.stringify(pluginRouteContext, null, 2));
29
36
  const routes = [];
30
37
  let globalData;
@@ -39,13 +46,14 @@ async function createPluginActionsUtils({ plugin, generatedFilesDir, baseUrl, tr
39
46
  ...finalRouteConfig,
40
47
  context: {
41
48
  ...(finalRouteConfig.context && { data: finalRouteConfig.context }),
42
- plugin: pluginRouteContextModulePath,
49
+ plugin: aliasedSource(pluginRouteContextModulePath),
43
50
  },
44
51
  });
45
52
  },
46
53
  async createData(name, data) {
47
54
  const modulePath = path_1.default.join(dataDir, name);
48
- await (0, utils_1.generate)(dataDir, name, data);
55
+ const dataString = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
56
+ await (0, utils_1.generate)(dataDir, name, dataString);
49
57
  return modulePath;
50
58
  },
51
59
  setGlobalData(data) {
@@ -4,10 +4,10 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import type { LoadContext, RouteConfig, GlobalData, PluginIdentifier, LoadedPlugin } from '@docusaurus/types';
7
+ import type { LoadContext, GlobalData, PluginIdentifier, LoadedPlugin, PluginRouteConfig } from '@docusaurus/types';
8
8
  export type LoadPluginsResult = {
9
9
  plugins: LoadedPlugin[];
10
- routes: RouteConfig[];
10
+ routes: PluginRouteConfig[];
11
11
  globalData: GlobalData;
12
12
  };
13
13
  /**
@@ -101,33 +101,30 @@ async function executePluginAllContentLoaded({ plugin, context, allContent, }) {
101
101
  async function executeAllPluginsAllContentLoaded({ plugins, context, }) {
102
102
  return utils_1.PerfLogger.async(`allContentLoaded()`, async () => {
103
103
  const allContent = (0, pluginsUtils_1.aggregateAllContent)(plugins);
104
- const routes = [];
105
- const globalData = {};
104
+ const allRoutes = [];
105
+ const allGlobalData = {};
106
106
  await Promise.all(plugins.map(async (plugin) => {
107
107
  var _a;
108
- const { routes: pluginRoutes, globalData: pluginGlobalData } = await executePluginAllContentLoaded({
108
+ const { routes, globalData: pluginGlobalData } = await executePluginAllContentLoaded({
109
109
  plugin,
110
110
  context,
111
111
  allContent,
112
112
  });
113
- routes.push(...pluginRoutes);
113
+ const pluginRoutes = routes.map((route) => (0, pluginsUtils_1.toPluginRoute)({ plugin, route }));
114
+ allRoutes.push(...pluginRoutes);
114
115
  if (pluginGlobalData !== undefined) {
115
- globalData[_a = plugin.name] ?? (globalData[_a] = {});
116
- globalData[plugin.name][plugin.options.id] = pluginGlobalData;
116
+ allGlobalData[_a = plugin.name] ?? (allGlobalData[_a] = {});
117
+ allGlobalData[plugin.name][plugin.options.id] = pluginGlobalData;
117
118
  }
118
119
  }));
119
- return { routes, globalData };
120
+ return { routes: allRoutes, globalData: allGlobalData };
120
121
  });
121
122
  }
122
123
  // This merges plugins routes and global data created from both lifecycles:
123
124
  // - contentLoaded()
124
125
  // - allContentLoaded()
125
- function mergeResults({ plugins, allContentLoadedResult, }) {
126
- const routes = [
127
- ...(0, pluginsUtils_1.aggregateRoutes)(plugins),
128
- ...allContentLoadedResult.routes,
129
- ];
130
- (0, routeConfig_1.sortRoutes)(routes);
126
+ function mergeResults({ baseUrl, plugins, allContentLoadedResult, }) {
127
+ const routes = (0, routeConfig_1.sortRoutes)([...(0, pluginsUtils_1.aggregateRoutes)(plugins), ...allContentLoadedResult.routes], baseUrl);
131
128
  const globalData = (0, pluginsUtils_1.mergeGlobalData)((0, pluginsUtils_1.aggregateGlobalData)(plugins), allContentLoadedResult.globalData);
132
129
  return { routes, globalData };
133
130
  }
@@ -148,6 +145,7 @@ async function loadPlugins(context) {
148
145
  context,
149
146
  });
150
147
  const { routes, globalData } = mergeResults({
148
+ baseUrl: context.baseUrl,
151
149
  plugins,
152
150
  allContentLoadedResult,
153
151
  });
@@ -179,6 +177,7 @@ async function reloadPlugin({ pluginIdentifier, plugins: previousPlugins, contex
179
177
  context,
180
178
  });
181
179
  const { routes, globalData } = mergeResults({
180
+ baseUrl: context.baseUrl,
182
181
  plugins,
183
182
  allContentLoadedResult,
184
183
  });
@@ -4,13 +4,17 @@
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 { AllContent, GlobalData, InitializedPlugin, LoadedPlugin, PluginIdentifier, RouteConfig } from '@docusaurus/types';
7
+ import type { AllContent, GlobalData, InitializedPlugin, LoadedPlugin, PluginIdentifier, PluginRouteConfig, RouteConfig } from '@docusaurus/types';
8
8
  export declare function getPluginByIdentifier<P extends InitializedPlugin>({ plugins, pluginIdentifier, }: {
9
9
  pluginIdentifier: PluginIdentifier;
10
10
  plugins: P[];
11
11
  }): P;
12
12
  export declare function aggregateAllContent(loadedPlugins: LoadedPlugin[]): AllContent;
13
- export declare function aggregateRoutes(loadedPlugins: LoadedPlugin[]): RouteConfig[];
13
+ export declare function toPluginRoute({ plugin, route, }: {
14
+ plugin: LoadedPlugin;
15
+ route: RouteConfig;
16
+ }): PluginRouteConfig;
17
+ export declare function aggregateRoutes(loadedPlugins: LoadedPlugin[]): PluginRouteConfig[];
14
18
  export declare function aggregateGlobalData(loadedPlugins: LoadedPlugin[]): GlobalData;
15
19
  export declare function mergeGlobalData(...globalDataList: GlobalData[]): GlobalData;
16
20
  export declare function formatPluginName(plugin: InitializedPlugin | PluginIdentifier): string;
@@ -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.formatPluginName = exports.mergeGlobalData = exports.aggregateGlobalData = exports.aggregateRoutes = exports.aggregateAllContent = exports.getPluginByIdentifier = void 0;
9
+ exports.formatPluginName = exports.mergeGlobalData = exports.aggregateGlobalData = exports.aggregateRoutes = exports.toPluginRoute = exports.aggregateAllContent = exports.getPluginByIdentifier = void 0;
10
10
  const tslib_1 = require("tslib");
11
11
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
12
12
  const logger_1 = tslib_1.__importDefault(require("@docusaurus/logger"));
@@ -28,8 +28,12 @@ function aggregateAllContent(loadedPlugins) {
28
28
  .value();
29
29
  }
30
30
  exports.aggregateAllContent = aggregateAllContent;
31
+ function toPluginRoute({ plugin, route, }) {
32
+ return { plugin: { name: plugin.name, id: plugin.options.id }, ...route };
33
+ }
34
+ exports.toPluginRoute = toPluginRoute;
31
35
  function aggregateRoutes(loadedPlugins) {
32
- return loadedPlugins.flatMap((p) => p.routes);
36
+ return loadedPlugins.flatMap((plugin) => plugin.routes.map((route) => toPluginRoute({ plugin, route })));
33
37
  }
34
38
  exports.aggregateRoutes = aggregateRoutes;
35
39
  function aggregateGlobalData(loadedPlugins) {
@@ -7,5 +7,5 @@
7
7
  import { type ApplyTrailingSlashParams } from '@docusaurus/utils-common';
8
8
  import type { RouteConfig } from '@docusaurus/types';
9
9
  /** Recursively applies trailing slash config to all nested routes. */
10
- export declare function applyRouteTrailingSlash(route: RouteConfig, params: ApplyTrailingSlashParams): RouteConfig;
11
- export declare function sortRoutes(routeConfigs: RouteConfig[], baseUrl?: string): void;
10
+ export declare function applyRouteTrailingSlash<Route extends RouteConfig>(route: Route, params: ApplyTrailingSlashParams): Route;
11
+ export declare function sortRoutes<Route extends RouteConfig>(routesToSort: Route[], baseUrl: string): Route[];
@@ -19,7 +19,8 @@ function applyRouteTrailingSlash(route, params) {
19
19
  };
20
20
  }
21
21
  exports.applyRouteTrailingSlash = applyRouteTrailingSlash;
22
- function sortRoutes(routeConfigs, baseUrl = '/') {
22
+ function sortRoutes(routesToSort, baseUrl) {
23
+ const routeConfigs = [...routesToSort];
23
24
  // Sort the route config. This ensures that route with nested
24
25
  // routes is always placed last.
25
26
  routeConfigs.sort((a, b) => {
@@ -36,6 +37,22 @@ function sortRoutes(routeConfigs, baseUrl = '/') {
36
37
  if (!a.routes && b.routes) {
37
38
  return -1;
38
39
  }
40
+ // If both are parent routes (for example routeBasePath: "/" and "/docs/"
41
+ // We must order them carefully in case of overlapping paths
42
+ if (a.routes && b.routes) {
43
+ if (a.path === b.path) {
44
+ // We don't really support that kind of routing ATM
45
+ // React-Router by default will only "enter" a single parent route
46
+ }
47
+ else {
48
+ if (a.path.includes(b.path)) {
49
+ return -1;
50
+ }
51
+ if (b.path.includes(a.path)) {
52
+ return 1;
53
+ }
54
+ }
55
+ }
39
56
  // Higher priority get placed first.
40
57
  if (a.priority || b.priority) {
41
58
  const priorityA = a.priority ?? 0;
@@ -49,8 +66,9 @@ function sortRoutes(routeConfigs, baseUrl = '/') {
49
66
  });
50
67
  routeConfigs.forEach((routeConfig) => {
51
68
  if (routeConfig.routes) {
52
- sortRoutes(routeConfig.routes, baseUrl);
69
+ routeConfig.routes = sortRoutes(routeConfig.routes, baseUrl);
53
70
  }
54
71
  });
72
+ return routeConfigs;
55
73
  }
56
74
  exports.sortRoutes = sortRoutes;
package/lib/ssg.d.ts CHANGED
@@ -4,6 +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
+ /// <reference path="../src/deps.d.ts" />
7
8
  import type { AppRenderer, SiteCollectedData } from './common';
8
9
  import type { Manifest } from 'react-loadable-ssr-addon-v5-slorber';
9
10
  import type { SSRTemplateCompiled } from './templates/templates';
@@ -88,9 +88,9 @@ async function createBaseConfig({ props, isServer, minify, }) {
88
88
  },
89
89
  devtool: isProd ? undefined : 'eval-cheap-module-source-map',
90
90
  resolve: {
91
- unsafeCache: false,
91
+ unsafeCache: false, // Not enabled, does not seem to improve perf much
92
92
  extensions: ['.wasm', '.mjs', '.js', '.jsx', '.ts', '.tsx', '.json'],
93
- symlinks: true,
93
+ symlinks: true, // See https://github.com/facebook/docusaurus/issues/3272
94
94
  roots: [
95
95
  // Allow resolution of url("/fonts/xyz.ttf") by webpack
96
96
  // See https://webpack.js.org/configuration/resolve/#resolveroots
@@ -18,9 +18,9 @@ export declare function getBabelOptions({ isServer, babelOptions, }?: {
18
18
  isServer?: boolean;
19
19
  babelOptions?: TransformOptions | string;
20
20
  }): TransformOptions;
21
- export declare const getCustomizableJSLoader: (jsLoader?: "babel" | ((isServer: boolean) => RuleSetRule)) => ({ isServer, babelOptions, }: {
21
+ export declare const getCustomizableJSLoader: (jsLoader?: 'babel' | ((isServer: boolean) => RuleSetRule)) => ({ isServer, babelOptions, }: {
22
22
  isServer: boolean;
23
- babelOptions?: string | TransformOptions | undefined;
23
+ babelOptions?: TransformOptions | string;
24
24
  }) => RuleSetRule;
25
25
  /**
26
26
  * Helper function to modify webpack config
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.2.1",
4
+ "version": "3.3.0",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -43,14 +43,12 @@
43
43
  "@babel/runtime": "^7.22.6",
44
44
  "@babel/runtime-corejs3": "^7.22.6",
45
45
  "@babel/traverse": "^7.22.8",
46
- "@docusaurus/cssnano-preset": "3.2.1",
47
- "@docusaurus/logger": "3.2.1",
48
- "@docusaurus/mdx-loader": "3.2.1",
49
- "@docusaurus/react-loadable": "5.5.2",
50
- "@docusaurus/utils": "3.2.1",
51
- "@docusaurus/utils-common": "3.2.1",
52
- "@docusaurus/utils-validation": "3.2.1",
53
- "@svgr/webpack": "^6.5.1",
46
+ "@docusaurus/cssnano-preset": "3.3.0",
47
+ "@docusaurus/logger": "3.3.0",
48
+ "@docusaurus/mdx-loader": "3.3.0",
49
+ "@docusaurus/utils": "3.3.0",
50
+ "@docusaurus/utils-common": "3.3.0",
51
+ "@docusaurus/utils-validation": "3.3.0",
54
52
  "autoprefixer": "^10.4.14",
55
53
  "babel-loader": "^9.1.3",
56
54
  "babel-plugin-dynamic-import-node": "^2.3.3",
@@ -64,8 +62,8 @@
64
62
  "copy-webpack-plugin": "^11.0.0",
65
63
  "core-js": "^3.31.1",
66
64
  "css-loader": "^6.8.1",
67
- "css-minimizer-webpack-plugin": "^4.2.2",
68
- "cssnano": "^5.1.15",
65
+ "css-minimizer-webpack-plugin": "^5.0.1",
66
+ "cssnano": "^6.1.2",
69
67
  "del": "^6.1.1",
70
68
  "detect-port": "^1.5.1",
71
69
  "escape-html": "^1.0.3",
@@ -85,7 +83,7 @@
85
83
  "prompts": "^2.4.2",
86
84
  "react-dev-utils": "^12.0.1",
87
85
  "react-helmet-async": "^1.3.0",
88
- "react-loadable": "npm:@docusaurus/react-loadable@5.5.2",
86
+ "react-loadable": "npm:@docusaurus/react-loadable@6.0.0",
89
87
  "react-loadable-ssr-addon-v5-slorber": "^1.0.1",
90
88
  "react-router": "^5.3.4",
91
89
  "react-router-config": "^5.1.1",
@@ -105,8 +103,8 @@
105
103
  "webpackbar": "^5.0.2"
106
104
  },
107
105
  "devDependencies": {
108
- "@docusaurus/module-type-aliases": "3.2.1",
109
- "@docusaurus/types": "3.2.1",
106
+ "@docusaurus/module-type-aliases": "3.3.0",
107
+ "@docusaurus/types": "3.3.0",
110
108
  "@total-typescript/shoehorn": "^0.1.2",
111
109
  "@types/detect-port": "^1.3.3",
112
110
  "@types/react-dom": "^18.2.7",
@@ -126,5 +124,5 @@
126
124
  "engines": {
127
125
  "node": ">=18.0"
128
126
  },
129
- "gitHead": "f268e15264e208e6faf26117258162e988b53773"
127
+ "gitHead": "2ec4e078b5ca0c57f2cc04f2fe564d524bb5e858"
130
128
  }