Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(v2): allow home page for docs #2652

Merged
merged 7 commits into from
May 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@ Object {

exports[`simple website content 2`] = `
Array [
Object {
"component": "@theme/DocPage",
"exact": true,
"modules": Object {
"content": "@site/docs/hello.md",
"docsMetadata": "~docs/site-docs-hello-md-9df-base.json",
},
"path": "/docs",
},
Object {
"component": "@theme/DocPage",
"modules": Object {
Expand Down Expand Up @@ -107,6 +116,33 @@ Array [

exports[`versioned website content 1`] = `
Array [
Object {
"component": "@theme/DocPage",
"exact": true,
"modules": Object {
"content": "@site/versioned_docs/version-1.0.1/hello.md",
"docsMetadata": "~docs/site-versioned-docs-version-1-0-1-hello-md-0c7-base.json",
},
"path": "/docs",
},
Object {
"component": "@theme/DocPage",
"exact": true,
"modules": Object {
"content": "@site/versioned_docs/version-1.0.0/hello.md",
"docsMetadata": "~docs/site-versioned-docs-version-1-0-0-hello-md-3ef-base.json",
},
"path": "/docs/1.0.0",
},
Object {
"component": "@theme/DocPage",
"exact": true,
"modules": Object {
"content": "@site/docs/hello.md",
"docsMetadata": "~docs/site-docs-hello-md-9df-base.json",
},
"path": "/docs/next",
},
Object {
"component": "@theme/DocPage",
"modules": Object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ describe('simple website', () => {
const plugin = pluginContentDocs(context, {
path: pluginPath,
sidebarPath,
homePageId: 'hello',
});
const pluginContentDir = path.join(context.generatedFilesDir, plugin.name);

Expand Down Expand Up @@ -203,6 +204,9 @@ describe('simple website', () => {
expect(baseMetadata.docsSidebars).toEqual(docsSidebars);
expect(baseMetadata.permalinkToSidebar).toEqual(permalinkToSidebar);

// Sort the route config like in src/server/plugins/index.ts for consistent snapshot ordering
sortConfig(routeConfigs);

expect(routeConfigs).not.toEqual([]);
expect(routeConfigs).toMatchSnapshot();
});
Expand All @@ -216,6 +220,7 @@ describe('versioned website', () => {
const plugin = pluginContentDocs(context, {
routeBasePath,
sidebarPath,
homePageId: 'hello',
});
const env = loadEnv(siteDir);
const {docsDir: versionedDir} = env.versioning;
Expand Down
130 changes: 102 additions & 28 deletions packages/docusaurus-plugin-content-docs/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,12 @@ import {
import {Configuration} from 'webpack';
import {docsVersion} from './version';

const REVERSED_DOCS_HOME_PAGE_ID = '_index';

const DEFAULT_OPTIONS: PluginOptions = {
path: 'docs', // Path to data on filesystem, relative to site dir.
routeBasePath: 'docs', // URL Route.
homePageId: REVERSED_DOCS_HOME_PAGE_ID, // Document id for docs home page.
include: ['**/*.{md,mdx}'], // Extensions to include.
sidebarPath: '', // Path to sidebar configuration for showing a list of markdown pages.
docLayoutComponent: '@theme/DocPage',
Expand Down Expand Up @@ -308,28 +311,96 @@ export default function pluginContentDocs(
const aliasedSource = (source: string) =>
`~docs/${path.relative(dataDir, source)}`;

const createDocsBaseMetadata = (version?: string): DocsBaseMetadata => {
const {docsSidebars, permalinkToSidebar, versionToSidebars} = content;
const neededSidebars: Set<string> =
versionToSidebars[version!] || new Set();

return {
docsSidebars: version
? pick(docsSidebars, Array.from(neededSidebars))
: docsSidebars,
permalinkToSidebar: version
? pickBy(permalinkToSidebar, (sidebar) =>
neededSidebars.has(sidebar),
)
: permalinkToSidebar,
version,
};
};

const genRoutes = async (
metadataItems: Metadata[],
): Promise<RouteConfig[]> => {
const routes = await Promise.all(
metadataItems.map(async (metadataItem) => {
await createData(
// Note that this created data path must be in sync with
// metadataPath provided to mdx-loader.
`${docuHash(metadataItem.source)}.json`,
JSON.stringify(metadataItem, null, 2),
const routes: RouteConfig[] = [];

await metadataItems.forEach(async (metadataItem, i) => {
const isDocsHomePage =
metadataItem.id.substr(metadataItem.id.indexOf('/') + 1) ===
options.homePageId;

if (isDocsHomePage) {
const homeDocsRoutePath =
routeBasePath === '' ? '/' : routeBasePath;
const versionDocsPathPrefix =
(metadataItem?.version === versioning.latestVersion
? ''
: metadataItem.version!) ?? '';

// To show the sidebar, get the sidebar key of available sibling item.
metadataItem.sidebar = (
metadataItems[i - 1] ?? metadataItems[i + 1]
).sidebar;
const docsBaseMetadata = createDocsBaseMetadata(
metadataItem.version!,
);
docsBaseMetadata.isHomePage = true;
docsBaseMetadata.homePagePath = normalizeUrl([
baseUrl,
homeDocsRoutePath,
versionDocsPathPrefix,
options.homePageId,
]);
const docsBaseMetadataPath = await createData(
`${docuHash(metadataItem.source)}-base.json`,
JSON.stringify(docsBaseMetadata, null, 2),
);

return {
// Add a route for docs home page.
addRoute({
path: normalizeUrl([
baseUrl,
homeDocsRoutePath,
versionDocsPathPrefix,
]),
component: docLayoutComponent,
exact: true,
modules: {
docsMetadata: aliasedSource(docsBaseMetadataPath),
content: metadataItem.source,
},
});
}

await createData(
// Note that this created data path must be in sync with
// metadataPath provided to mdx-loader.
`${docuHash(metadataItem.source)}.json`,
JSON.stringify(metadataItem, null, 2),
);

// Do not create a route for a page created specifically for docs home page.
if (metadataItem.id !== REVERSED_DOCS_HOME_PAGE_ID) {
routes.push({
path: metadataItem.permalink,
component: docItemComponent,
exact: true,
modules: {
content: metadataItem.source,
},
};
}),
);
});
}
});

return routes.sort((a, b) =>
a.path > b.path ? 1 : b.path > a.path ? -1 : 0,
Expand Down Expand Up @@ -378,19 +449,7 @@ export default function pluginContentDocs(
isLatestVersion ? '' : version,
]);
const docsBaseRoute = normalizeUrl([docsBasePermalink, ':route']);
const neededSidebars: Set<string> =
content.versionToSidebars[version] || new Set();
const docsBaseMetadata: DocsBaseMetadata = {
docsSidebars: pick(
content.docsSidebars,
Array.from(neededSidebars),
),
permalinkToSidebar: pickBy(
content.permalinkToSidebar,
(sidebar) => neededSidebars.has(sidebar),
),
version,
};
const docsBaseMetadata = createDocsBaseMetadata(version);

// We want latest version route config to be placed last in the
// generated routeconfig. Otherwise, `/docs/next/foo` will match
Expand All @@ -405,16 +464,31 @@ export default function pluginContentDocs(
);
} else {
const routes = await genRoutes(Object.values(content.docsMetadata));
const docsBaseMetadata: DocsBaseMetadata = {
docsSidebars: content.docsSidebars,
permalinkToSidebar: content.permalinkToSidebar,
};
const docsBaseMetadata = createDocsBaseMetadata();

const docsBaseRoute = normalizeUrl([baseUrl, routeBasePath, ':route']);
return addBaseRoute(docsBaseRoute, docsBaseMetadata, routes);
}
},

async routesLoaded(routes) {
const normalizedHomeDocsRoutePath = `/${options.routeBasePath}`;
const homeDocsRoutes = routes.filter(
(routeConfig) => routeConfig.path === normalizedHomeDocsRoutePath,
);

// Remove the route for docs home page if there is a page with the same path (i.e. docs).
if (homeDocsRoutes.length > 1) {
const docsHomePageRouteIndex = routes.findIndex(
(route) =>
route.component === options.docLayoutComponent &&
route.path === normalizedHomeDocsRoutePath,
);

delete routes[docsHomePageRouteIndex!];
}
},

configureWebpack(_config, isServer, utils) {
const {getBabelLoader, getCacheLoader} = utils;
const {rehypePlugins, remarkPlugins} = options;
Expand Down
3 changes: 3 additions & 0 deletions packages/docusaurus-plugin-content-docs/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface PluginOptions extends MetadataOptions, PathOptions {
remarkPlugins: ([Function, object] | Function)[];
rehypePlugins: string[];
admonitions: any;
homePageId: string;
}

export type SidebarItemDoc = {
Expand Down Expand Up @@ -160,6 +161,8 @@ export type DocsBaseMetadata = Pick<
'docsSidebars' | 'permalinkToSidebar'
> & {
version?: string;
isHomePage?: boolean;
homePagePath?: string;
};

export type VersioningEnv = {
Expand Down
41 changes: 28 additions & 13 deletions packages/docusaurus-theme-classic/src/theme/DocPage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {MDXProvider} from '@mdx-js/react';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import renderRoutes from '@docusaurus/renderRoutes';
import Layout from '@theme/Layout';
import DocItem from '@theme/DocItem';
import DocSidebar from '@theme/DocSidebar';
import MDXComponents from '@theme/MDXComponents';
import NotFound from '@theme/NotFound';
Expand All @@ -19,21 +20,31 @@ import {matchPath} from '@docusaurus/router';
import styles from './styles.module.css';

function DocPage(props) {
const {route: baseRoute, docsMetadata, location} = props;
// case-sensitive route such as it is defined in the sidebar
const currentRoute =
baseRoute.routes.find((route) => {
return matchPath(location.pathname, route);
}) || {};
const {permalinkToSidebar, docsSidebars, version} = docsMetadata;
const sidebar = permalinkToSidebar[currentRoute.path];
const {route: baseRoute, docsMetadata, location, content} = props;
const {
siteConfig: {themeConfig = {}} = {},
permalinkToSidebar,
docsSidebars,
version,
isHomePage,
homePagePath,
} = docsMetadata;

// Get case-sensitive route such as it is defined in the sidebar.
const currentRoute = !isHomePage
? baseRoute.routes.find((route) => {
return matchPath(location.pathname, route);
}) || {}
: {};

const sidebar = isHomePage
? content.metadata.sidebar
: permalinkToSidebar[currentRoute.path];
const {
siteConfig: {themeConfig: {sidebarCollapsible = true} = {}} = {},
isClient,
} = useDocusaurusContext();
const {sidebarCollapsible = true} = themeConfig;

if (Object.keys(currentRoute).length === 0) {
if (!isHomePage && Object.keys(currentRoute).length === 0) {
return <NotFound {...props} />;
}

Expand All @@ -44,15 +55,19 @@ function DocPage(props) {
<div className={styles.docSidebarContainer}>
<DocSidebar
docsSidebars={docsSidebars}
path={currentRoute.path}
path={isHomePage ? homePagePath : currentRoute.path}
sidebar={sidebar}
sidebarCollapsible={sidebarCollapsible}
/>
</div>
)}
<main className={styles.docMainContainer}>
<MDXProvider components={MDXComponents}>
{renderRoutes(baseRoute.routes)}
{isHomePage ? (
<DocItem content={content} />
) : (
renderRoutes(baseRoute.routes)
)}
</MDXProvider>
</main>
</div>
Expand Down
14 changes: 12 additions & 2 deletions packages/docusaurus-theme-classic/src/theme/DocSidebar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import styles from './styles.module.css';

const MOBILE_TOGGLE_SIZE = 24;

function DocSidebarItem({item, onItemClick, collapsible, ...props}) {
function DocSidebarItem({
item,
onItemClick,
collapsible,
activePath,
...props
}) {
const {items, href, label, type} = item;
const [collapsed, setCollapsed] = useState(item.collapsed);
const [prevCollapsedProp, setPreviousCollapsedProp] = useState(null);
Expand Down Expand Up @@ -63,6 +69,7 @@ function DocSidebarItem({item, onItemClick, collapsible, ...props}) {
item={childItem}
onItemClick={onItemClick}
collapsible={collapsible}
activePath={activePath}
/>
))}
</ul>
Expand All @@ -75,7 +82,9 @@ function DocSidebarItem({item, onItemClick, collapsible, ...props}) {
return (
<li className="menu__list-item" key={label}>
<Link
className="menu__link"
className={classnames('menu__link', {
'menu__link--active': href === activePath,
})}
to={href}
{...(isInternalUrl(href)
? {
Expand Down Expand Up @@ -219,6 +228,7 @@ function DocSidebar(props) {
setShowResponsiveSidebar(false);
}}
collapsible={sidebarCollapsible}
activePath={path}
/>
))}
</ul>
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export interface Plugin<T> {
content: T;
actions: PluginContentLoadedActions;
}): void;
routesLoaded?(routes: RouteConfig[]): void;
postBuild?(props: Props): void;
postStart?(props: Props): void;
configureWebpack?(
Expand Down
Loading