Skip to content

Commit

Permalink
feat: support api page multi group (#94)
Browse files Browse the repository at this point in the history
* feat: support api page multi group

* fix: api page empty in production
  • Loading branch information
sanyuan0704 committed Oct 15, 2022
1 parent 0b726e3 commit 9d038b2
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 50 deletions.
19 changes: 18 additions & 1 deletion docs/en/guide/api-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,21 @@ pageType: api
---
```

In this way, Island.js will automatically parse the document structure of other subpages in the directory for you, extract the title (h1, h2) information, and generate the API page.
In this way, Island.js will automatically parse the document structure of other subpages in the directory for you, extract the title (h1, h2) information, and generate the API page.What's more, Island.js will group the API documentation according to your [`sidebar`](/en/api/config-theme#sidebar) config in the config file. For example, the following `sidebar` config:

```ts
{
'/api/': [
{
text: 'Config',
items: [{ text: '1', link: '/api/1' }, { text: '2', link: '/api/2' }]
},
{
text: 'Runtime',
items: [{ text: '3', link: '/api/3' }, { text: '4', link: '/api/4' }]
}
];
}
```

Then the API page will display two groups of `Config` and `Runtime`.
19 changes: 18 additions & 1 deletion docs/zh/guide/api-page.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,21 @@ pageType: api
---
```

这样 Island.js 会自动为你解析该目录下其它子页面的文档结构,提取出其中的标题(h1、h2)信息,并生成 API 页面。
这样 Island.js 会自动为你解析该目录下其它子页面的文档结构,提取出其中的标题(h1、h2)信息,并生成 API 页面,并且,会根据你在配置文件中的 [`sidebar`](/en/api/config-theme#sidebar) 配置来对 API 文档进行分组展示。比如有如下的 `sidebar` 配置:

```ts
{
'/api/': [
{
text: 'Config',
items: [{ text: '1', link: '/api/1' }, { text: '2', link: '/api/2' }]
},
{
text: 'Runtime',
items: [{ text: '3', link: '/api/3' }, { text: '4', link: '/api/4' }]
}
];
}
```

那么在 API 页面将会展示 `Config``Runtime` 两个分组。
1 change: 0 additions & 1 deletion src/node/__tests__/babel-plugin-island.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ describe('test babel-plugin-island', () => {
test('Should compile jsx identifier', async () => {
const code = `import A from '${ISLAND_PATH}'; export default function App() { return <A __island>hello</A>; }`;
const result = await transformAsync(code, babelOptions);
console.log(result?.code);
expect(result?.code).toContain(
`__island: "${ISLAND_PATH}${MASK_SPLITTER}${IMPORTER_PATH}"`
);
Expand Down
1 change: 0 additions & 1 deletion src/node/babel-plugin-island.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export default declare((api) => {
for (let i = 0; i < attributes.length; i++) {
const name = (attributes[i] as t.JSXAttribute).name;
if (name?.name === ID) {
console.log(state.filename);
(attributes[i] as t.JSXAttribute).value = t.stringLiteral(
`${source.value}${MASK_SPLITTER}${normalizePath(
state.filename || ''
Expand Down
110 changes: 68 additions & 42 deletions src/theme-default/layout/APILayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,65 @@
import { usePageData } from 'island/client';
import { useEffect, useState } from 'react';
import { Header } from 'shared/types';
import { normalizeHref } from '../../logic';
import { ComponentType, useEffect, useState } from 'react';
import { Header, PageModule } from 'shared/types';
import { normalizeHref, useSidebarData } from '../../logic';
import { Link } from '../../components/Link/index';
import styles from './index.module.scss';
import { useLocation } from 'react-router-dom';
import { isEqualPath } from '../../logic/utils';

interface GroupItem {
text: string;
link: string;
headers: Header[];
text?: string;
link?: string;
headers?: Header[];
}

interface Group {
name: string;
items: GroupItem[];
}

export function APILayout() {
const { subModules = [] } = usePageData();
const { subModules: apiPageModules = [] } = usePageData();
const { pathname } = useLocation();
const { items: apiSidebarGroups } = useSidebarData(pathname);

const initialGroups: Group[] = apiSidebarGroups.map((sidebarGroup) => ({
name: sidebarGroup.text || '',
items: sidebarGroup.items.map((item) => {
const pageModule = apiPageModules.find((m) =>
isEqualPath(m.routePath as string, item.link || '')
);
return {
...item,
headers: (pageModule?.toc as Header[]).filter(
(header) => header.depth === 2
)
};
})
}));

const initialGroups = subModules.map((subModule) => {
const { routePath, title } = subModule;
return {
text: title,
link: routePath,
headers: (subModule.toc as Header[]).filter((h) => h.depth === 2)
} as GroupItem;
});
const [groups, setGroups] = useState(initialGroups);

useEffect(() => {
// Handle title hmr
if (import.meta.env.DEV) {
import.meta.hot?.on('md(x)-changed', ({ routePath, filePath }) => {
const group = groups.find((group) => group.link === routePath);
const group = groups.find((group) =>
group.items.find((item) => item.link === routePath)
);
if (!group) {
return;
}
import(/* @vite-ignore */ `${filePath}?import&t=${Date.now()}`).then(
(mod) => {
group.headers = mod.toc;
group.text = mod.title;
(mod: PageModule<ComponentType<unknown>>) => {
const itemIndex = group.items.findIndex(
(item) => item.link === routePath
);
const targetItem = group.items[itemIndex];
targetItem.headers = (mod?.toc as Header[]).filter(
(header) => header.depth === 2
);
targetItem.text = mod?.title as string;
setGroups([...groups]);
}
);
Expand All @@ -48,30 +72,32 @@ export function APILayout() {
<h1>API Reference</h1>
</div>

<div mb="16">
<h2>Config</h2>
<div className={styles.apiGroups}>
{groups.map((item) => (
<div className={styles.apiGroup} key={item.link}>
<h3>{item.text}</h3>
<ul list="none">
{item.headers?.map((header) => (
<li
key={header.id}
className={`${styles.apiGroupLi} ${
styles[`level${header.depth}`]
}`}
>
<Link href={`${normalizeHref(item.link)}#${header.id}`}>
{header.text}
</Link>
</li>
))}
</ul>
</div>
))}
{groups.map((group) => (
<div mb="16" key={group.name!}>
<h2>{group.name}</h2>
<div className={styles.apiGroups}>
{group.items.map((item) => (
<div className={styles.apiGroup} key={item.link}>
<h3>{item.text}</h3>
<ul list="none">
{item.headers?.map((header) => (
<li
key={header.id}
className={`${styles.apiGroupLi} ${
styles[`level${header.depth}`]
}`}
>
<Link href={`${normalizeHref(item.link)}#${header.id}`}>
{header.text}
</Link>
</li>
))}
</ul>
</div>
))}
</div>
</div>
</div>
))}
</div>
);
}
16 changes: 12 additions & 4 deletions src/theme-default/logic/useSidebarData.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
import { DefaultTheme } from 'shared/types/default-theme';
import { normalizeHref } from './index';
import { useLocaleSiteData } from './useLocaleSiteData';
import { isEqualPath } from './utils';

interface SitebarData {
interface SidebarData {
// The group name ofr the sidebar
group: string;
items: DefaultTheme.SidebarGroup[];
}

export function useSidebarData(currentPathname: string): SitebarData {
export function useSidebarData(currentPathname: string): SidebarData {
const localeData = useLocaleSiteData();
const sidebar = localeData.sidebar ?? {};
for (const name of Object.keys(sidebar)) {
// Such as `/api/`,it will return all the sidebar group
if (isEqualPath(name, currentPathname)) {
return {
group: '',
items: sidebar[name]
};
}
// Such as `/guide/getting-started`, it will return the guide groups and the group name `Introduction`
const result = sidebar[name].find((group) =>
group.items.some(
(item) => normalizeHref(item.link) === normalizeHref(currentPathname)
(item) => item.link && isEqualPath(item.link, currentPathname)
)
);
if (result) {
Expand Down
4 changes: 4 additions & 0 deletions src/theme-default/logic/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export function normalizeHref(url?: string) {
return addLeadingSlash(`${url}${suffix}`);
}

export function isEqualPath(a: string, b: string) {
return normalizeHref(a) === normalizeHref(b);
}

export function backTrackHeaders(
rawHeaders: Header[],
index: number
Expand Down

1 comment on commit 9d038b2

@vercel
Copy link

@vercel vercel bot commented on 9d038b2 Oct 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.