Skip to content

Commit

Permalink
Fix blank top-level 404 page (#84)
Browse files Browse the repository at this point in the history
Fixes #7 in an
unfortunately clever, yet still appropriate way.

See the comment in `src/js/docsPluginWithTopLevel404.js` for greater
detail.

Tests:
- http://localhost:3000 -> ✔️ 
- http://localhost:3000/ -> (redirects to http://localhost:3000)
- http://localhost:3000/2.20/docs/introduction/welcome-to-pants ->
✔️
- http://localhost:3000/2.20/nope -> (Page Not Found) ✔️
- http://localhost:3000/nope -> (Page Not Found) ✔️ 
- http://localhost:3000/sponsorship -> ✔️
  • Loading branch information
thejcannon authored Jan 11, 2024
1 parent 838b57d commit 3ddb2eb
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 41 deletions.
86 changes: 45 additions & 41 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,47 +68,7 @@ const config = {
"@docusaurus/preset-classic",
{
debug: process.env.NODE_ENV !== "production",
docs: {
sidebarPath: require.resolve("./sidebars.js"),
routeBasePath: "/",
disableVersioning,
onlyIncludeVersions,
lastVersion: onlyIncludeVersions ? undefined : versions[1],
versions: {
current: {
label: `${currentVersion} (dev)`,
path: currentVersion,
},
...(disableVersioning
? {}
: versions.reduce((acc, version, index) => {
acc[version] = {
label:
index === 0
? `${version} (prerelease)`
: index < 3
? version
: `${version} (deprecated)`,
banner:
index == 0
? "unreleased"
: index < 3
? "none"
: "unmaintained",
noIndex: index >= 3,
path: version,
};
return acc;
}, {})),
},
remarkPlugins: [captionedCode],
editUrl: ({ docPath }) => {
if (docPath.startsWith("reference/")) {
return undefined;
}
return `https://github.com/pantsbuild/pants/edit/main/docs/${docPath}`;
},
},
docs: false, // NB: See `docsPluginWithTopLevel404.js` reference below
blog: includeBlog && {
showReadingTime: true,
editUrl: "https://github.com/pantsbuild/pantsbuild.org/edit/main/",
Expand Down Expand Up @@ -304,6 +264,50 @@ const config = {
},
},
plugins: [
[
"./src/js/docsPluginWithTopLevel404.js",
{
sidebarPath: require.resolve("./sidebars.js"),
routeBasePath: "/",
disableVersioning,
onlyIncludeVersions,
lastVersion: onlyIncludeVersions ? undefined : versions[1],
versions: {
current: {
label: `${currentVersion} (dev)`,
path: currentVersion,
},
...(disableVersioning
? {}
: versions.reduce((acc, version, index) => {
acc[version] = {
label:
index === 0
? `${version} (prerelease)`
: index < 3
? version
: `${version} (deprecated)`,
banner:
index == 0
? "unreleased"
: index < 3
? "none"
: "unmaintained",
noIndex: index >= 3,
path: version,
};
return acc;
}, {})),
},
remarkPlugins: [captionedCode],
editUrl: ({ docPath }) => {
if (docPath.startsWith("reference/")) {
return undefined;
}
return `https://github.com/pantsbuild/pants/edit/main/docs/${docPath}`;
},
},
],
[
"@docusaurus/plugin-client-redirects",
{
Expand Down
56 changes: 56 additions & 0 deletions src/js/docsPluginWithTopLevel404.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { default as pluginContentDocs } from "@docusaurus/plugin-content-docs";

/*
You: WTH is going on here?!
Me: Ok, umm, please let me explain...
So, there's this bug: https://github.com/facebook/docusaurus/issues/9688. Basically, because we
use `routeBasePath` set to `/`, the docs plugin hogs all `/*` routes that aren't hogged by another
plugin. But that means that "bad" top-level URLs like `/foo` render empty.
You: (nodding)
Me: It's complicated, you can see the comments from the maintainers. Something about `react-router-config`.
Anyways, there was a suggestion about how to fix it by "automatically add[ing] a `*` not found route
to all subroutes". This emulates that behavior.
You: I think I get it, but how does this work, exactly?
Me: Ah yeah. So firstly, we use this plugin instead of the docs plugin. This plugin is basically a
re-export of the docs plugin, but with the `addRoute` plugin API monkeypatched. The monkeypatching
adds a `*` not found route as a subroute.
You: I see. Well, OK I guess. Carry on.
*/
export default async function patchedPluginContentDocs(context, options) {
const result = await pluginContentDocs(context, options);
const actualContentLoaded = result.contentLoaded;
result.contentLoaded = async function ({ content, actions }) {
let docsRouteConfig = undefined;
const actualAddRoute = actions.addRoute;
actions.addRoute = function (routeConfig) {
if (docsRouteConfig !== undefined) {
throw "Expected only one call to addRoute!";
}
docsRouteConfig = routeConfig;
};
const result = await actualContentLoaded({ content, actions });
docsRouteConfig.routes.push({
path: "/*",
component: "@theme/NotFound/Content",
/* NB: Routes are sorted based on a heuristic: https://github.com/facebook/docusaurus/blob/d94adf6a6c925f2bd5ef09a75c5e8a66a0b7e7f9/packages/docusaurus/src/server/plugins/routeConfig.ts#L30
Our route must come last based on this heuristic. In order to accomplish that:
- We must have at least one route, or else we unconditionally come first.
It'll never get matched, but still must be valid
- Our priority must be _lower_ (with the default of 0).
*/
priority: -1,
routes: [
{
path: "/*",
component: "@theme/NotFound/Content",
},
],
});

actualAddRoute(docsRouteConfig);
return result;
};
return result;
}

export { validateOptions } from "@docusaurus/plugin-content-docs";

0 comments on commit 3ddb2eb

Please sign in to comment.