mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-19 21:13:30 +01:00
refactor: memoize everything
This commit is contained in:
@@ -21,5 +21,5 @@ export function RouterTransition() {
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [router.asPath]);
|
}, [router.asPath]);
|
||||||
|
|
||||||
return <NavigationProgress />;
|
return <NavigationProgress color="blurple" />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createStyles, Group, Text, NavLink, Box } from '@mantine/core';
|
import { createStyles, Group, Text, NavLink, Box } from '@mantine/core';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { type Dispatch, type SetStateAction, useEffect, useState } from 'react';
|
import { type Dispatch, type SetStateAction, useEffect, useState, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
VscSymbolClass,
|
VscSymbolClass,
|
||||||
VscSymbolEnum,
|
VscSymbolEnum,
|
||||||
@@ -101,7 +101,7 @@ export function SidebarItems({
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
const groupItems = groupMembers(members);
|
const groupItems = useMemo(() => groupMembers(members), [members]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0] ?? '');
|
setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0] ?? '');
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
|||||||
import Image from 'next/future/image';
|
import Image from 'next/future/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { type PropsWithChildren, useState, useEffect } from 'react';
|
import { type PropsWithChildren, useState, useEffect, useMemo } from 'react';
|
||||||
import { VscChevronDown, VscGithubInverted, VscPackage, VscVersions } from 'react-icons/vsc';
|
import { VscChevronDown, VscGithubInverted, VscPackage, VscVersions } from 'react-icons/vsc';
|
||||||
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
|
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
@@ -170,18 +170,25 @@ export function SidebarLayout({
|
|||||||
|
|
||||||
const { classes } = useStyles({ openedLib: openedLibPicker, openedVersion: openedVersionPicker });
|
const { classes } = useStyles({ openedLib: openedLibPicker, openedVersion: openedVersionPicker });
|
||||||
|
|
||||||
const versionMenuItems =
|
const versionMenuItems = useMemo(
|
||||||
versions?.map((item) => (
|
() =>
|
||||||
<Menu.Item key={item} component={NextLink} href={`/docs/packages/${packageName ?? 'builders'}/${item}`}>
|
versions?.map((item) => (
|
||||||
{item}
|
<Menu.Item key={item} component={NextLink} href={`/docs/packages/${packageName ?? 'builders'}/${item}`}>
|
||||||
</Menu.Item>
|
{item}
|
||||||
)) ?? [];
|
</Menu.Item>
|
||||||
|
)) ?? [],
|
||||||
|
[versions],
|
||||||
|
);
|
||||||
|
|
||||||
const breadcrumbs = asPathWithoutQueryAndAnchor.split('/').map((path, idx, original) => (
|
const breadcrumbs = useMemo(
|
||||||
<Link key={idx} href={original.slice(0, idx + 1).join('/')} passHref>
|
() =>
|
||||||
<Anchor component="a">{path}</Anchor>
|
asPathWithoutQueryAndAnchor.split('/').map((path, idx, original) => (
|
||||||
</Link>
|
<Link key={idx} href={original.slice(0, idx + 1).join('/')} passHref>
|
||||||
));
|
<Anchor component="a">{path}</Anchor>
|
||||||
|
</Link>
|
||||||
|
)),
|
||||||
|
[asPathWithoutQueryAndAnchor],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppShell
|
<AppShell
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createStyles, Group, Text, Box, Stack, ThemeIcon, useMantineColorScheme } from '@mantine/core';
|
import { createStyles, Group, Text, Box, Stack, ThemeIcon, useMantineColorScheme } from '@mantine/core';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import { VscListSelection, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
|
import { VscListSelection, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
|
||||||
import type { ApiClassJSON, ApiInterfaceJSON } from '~/DocModel/ApiNodeJSONEncoder';
|
import type { ApiClassJSON, ApiInterfaceJSON } from '~/DocModel/ApiNodeJSONEncoder';
|
||||||
|
|
||||||
@@ -36,34 +37,44 @@ export function TableOfContentItems({
|
|||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
const { classes } = useStyles();
|
const { classes } = useStyles();
|
||||||
|
|
||||||
const propertyItems = properties.map((prop) => (
|
const propertyItems = useMemo(
|
||||||
<Box<'a'> key={prop.name} href={`#${prop.name}`} component="a" className={classes.link}>
|
() =>
|
||||||
<Group>
|
properties.map((prop) => (
|
||||||
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
<Box<'a'> key={prop.name} href={`#${prop.name}`} component="a" className={classes.link}>
|
||||||
{prop.name}
|
<Group>
|
||||||
</Text>
|
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
||||||
</Group>
|
{prop.name}
|
||||||
</Box>
|
|
||||||
));
|
|
||||||
|
|
||||||
const methodItems = methods.map((member) => {
|
|
||||||
const key = `${member.name}${member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : ''}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Box<'a'> key={key} component="a" href={`#${key}`} className={classes.link}>
|
|
||||||
<Group>
|
|
||||||
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
|
||||||
{member.name}
|
|
||||||
</Text>
|
|
||||||
{member.overloadIndex && member.overloadIndex > 1 ? (
|
|
||||||
<Text size="xs" color="dimmed">
|
|
||||||
{member.overloadIndex}
|
|
||||||
</Text>
|
</Text>
|
||||||
) : null}
|
</Group>
|
||||||
</Group>
|
</Box>
|
||||||
</Box>
|
)),
|
||||||
);
|
[properties],
|
||||||
});
|
);
|
||||||
|
|
||||||
|
const methodItems = useMemo(
|
||||||
|
() =>
|
||||||
|
methods.map((member) => {
|
||||||
|
const key = `${member.name}${
|
||||||
|
member.overloadIndex && member.overloadIndex > 1 ? `:${member.overloadIndex}` : ''
|
||||||
|
}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box<'a'> key={key} component="a" href={`#${key}`} className={classes.link}>
|
||||||
|
<Group>
|
||||||
|
<Text sx={{ textOverflow: 'ellipsis', overflow: 'hidden' }} className="line-clamp-1">
|
||||||
|
{member.name}
|
||||||
|
</Text>
|
||||||
|
{member.overloadIndex && member.overloadIndex > 1 ? (
|
||||||
|
<Text size="xs" color="dimmed">
|
||||||
|
{member.overloadIndex}
|
||||||
|
</Text>
|
||||||
|
) : null}
|
||||||
|
</Group>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
[methods],
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ wordBreak: 'break-all' }} pb="xl">
|
<Box sx={{ wordBreak: 'break-all' }} pb="xl">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Anchor, Box, Code, Text, useMantineColorScheme } from '@mantine/core';
|
import { Anchor, Box, Code, Text, useMantineColorScheme } from '@mantine/core';
|
||||||
import { DocNodeKind, StandardTags } from '@microsoft/tsdoc';
|
import { DocNodeKind, StandardTags } from '@microsoft/tsdoc';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { Fragment, type ReactNode } from 'react';
|
import { Fragment, useCallback, type ReactNode } from 'react';
|
||||||
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
|
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
|
||||||
import { vscDarkPlus, ghcolors } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
import { vscDarkPlus, ghcolors } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||||
import { BlockComment } from './BlockComment';
|
import { BlockComment } from './BlockComment';
|
||||||
@@ -16,105 +16,108 @@ import type { DocCommentJSON } from '~/DocModel/comment/RootComment';
|
|||||||
export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
||||||
const { colorScheme } = useMantineColorScheme();
|
const { colorScheme } = useMantineColorScheme();
|
||||||
|
|
||||||
let numberOfExamples = 0;
|
const createNode = useCallback(
|
||||||
let exampleIndex = 0;
|
(node: AnyDocNodeJSON, idx?: number): ReactNode => {
|
||||||
|
let numberOfExamples = 0;
|
||||||
|
let exampleIndex = 0;
|
||||||
|
|
||||||
const createNode = (node: AnyDocNodeJSON, idx?: number): ReactNode => {
|
switch (node.kind) {
|
||||||
switch (node.kind) {
|
case DocNodeKind.PlainText:
|
||||||
case DocNodeKind.PlainText:
|
|
||||||
return (
|
|
||||||
<Text key={idx} span style={{ wordBreak: 'break-word' }}>
|
|
||||||
{(node as DocPlainTextJSON).text}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
case DocNodeKind.Paragraph:
|
|
||||||
return (
|
|
||||||
<Text key={idx} inline style={{ wordBreak: 'break-word' }}>
|
|
||||||
{(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))}
|
|
||||||
</Text>
|
|
||||||
);
|
|
||||||
case DocNodeKind.SoftBreak:
|
|
||||||
return <Fragment key={idx} />;
|
|
||||||
case DocNodeKind.LinkTag: {
|
|
||||||
const { codeDestination, urlDestination, text } = node as DocLinkTagJSON;
|
|
||||||
|
|
||||||
if (codeDestination) {
|
|
||||||
return (
|
return (
|
||||||
<Link key={idx} href={codeDestination.path} passHref>
|
<Text key={idx} span style={{ wordBreak: 'break-word' }}>
|
||||||
<Anchor component="a" className="font-mono">
|
{(node as DocPlainTextJSON).text}
|
||||||
{text ?? codeDestination.name}
|
</Text>
|
||||||
</Anchor>
|
);
|
||||||
</Link>
|
case DocNodeKind.Paragraph:
|
||||||
|
return (
|
||||||
|
<Text key={idx} inline style={{ wordBreak: 'break-word' }}>
|
||||||
|
{(node as DocNodeContainerJSON).nodes.map((node, idx) => createNode(node, idx))}
|
||||||
|
</Text>
|
||||||
|
);
|
||||||
|
case DocNodeKind.SoftBreak:
|
||||||
|
return <Fragment key={idx} />;
|
||||||
|
case DocNodeKind.LinkTag: {
|
||||||
|
const { codeDestination, urlDestination, text } = node as DocLinkTagJSON;
|
||||||
|
|
||||||
|
if (codeDestination) {
|
||||||
|
return (
|
||||||
|
<Link key={idx} href={codeDestination.path} passHref>
|
||||||
|
<Anchor component="a" className="font-mono">
|
||||||
|
{text ?? codeDestination.name}
|
||||||
|
</Anchor>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlDestination) {
|
||||||
|
return (
|
||||||
|
<Link key={idx} href={urlDestination} passHref>
|
||||||
|
<Anchor component="a" className="font-mono">
|
||||||
|
{text ?? urlDestination}
|
||||||
|
</Anchor>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
case DocNodeKind.CodeSpan: {
|
||||||
|
const { code } = node as DocFencedCodeJSON;
|
||||||
|
return (
|
||||||
|
<Code key={idx} sx={{ display: 'inline' }} className="text-sm font-mono">
|
||||||
|
{code}
|
||||||
|
</Code>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case DocNodeKind.FencedCode: {
|
||||||
if (urlDestination) {
|
const { language, code } = node as DocFencedCodeJSON;
|
||||||
return (
|
return (
|
||||||
<Link key={idx} href={urlDestination} passHref>
|
<SyntaxHighlighter
|
||||||
<Anchor component="a" className="font-mono">
|
key={idx}
|
||||||
{text ?? urlDestination}
|
wrapLines
|
||||||
</Anchor>
|
wrapLongLines
|
||||||
</Link>
|
language={language}
|
||||||
|
style={colorScheme === 'dark' ? vscDarkPlus : ghcolors}
|
||||||
|
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
||||||
|
>
|
||||||
|
{code}
|
||||||
|
</SyntaxHighlighter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
case DocNodeKind.ParamBlock:
|
||||||
|
case DocNodeKind.Block: {
|
||||||
|
const { tag } = node as DocBlockJSON;
|
||||||
|
|
||||||
return null;
|
if (tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase) {
|
||||||
}
|
exampleIndex++;
|
||||||
case DocNodeKind.CodeSpan: {
|
}
|
||||||
const { code } = node as DocFencedCodeJSON;
|
|
||||||
return (
|
|
||||||
<Code key={idx} sx={{ display: 'inline' }} className="text-sm font-mono">
|
|
||||||
{code}
|
|
||||||
</Code>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case DocNodeKind.FencedCode: {
|
|
||||||
const { language, code } = node as DocFencedCodeJSON;
|
|
||||||
return (
|
|
||||||
<SyntaxHighlighter
|
|
||||||
key={idx}
|
|
||||||
wrapLines
|
|
||||||
wrapLongLines
|
|
||||||
language={language}
|
|
||||||
style={colorScheme === 'dark' ? vscDarkPlus : ghcolors}
|
|
||||||
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
|
|
||||||
>
|
|
||||||
{code}
|
|
||||||
</SyntaxHighlighter>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case DocNodeKind.ParamBlock:
|
|
||||||
case DocNodeKind.Block: {
|
|
||||||
const { tag } = node as DocBlockJSON;
|
|
||||||
|
|
||||||
if (tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase) {
|
const index = numberOfExamples > 1 ? exampleIndex : undefined;
|
||||||
exampleIndex++;
|
|
||||||
|
return (
|
||||||
|
<BlockComment tagName={tag.tagName} key={idx} index={index}>
|
||||||
|
{(node as DocBlockJSON).content.map((node, idx) => createNode(node, idx))}
|
||||||
|
</BlockComment>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
case DocNodeKind.Comment: {
|
||||||
|
const comment = node as DocCommentJSON;
|
||||||
|
// Cheat a bit by finding out how many comments we have beforehand...
|
||||||
|
numberOfExamples = comment.customBlocks.filter(
|
||||||
|
(block) => block.tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase,
|
||||||
|
).length;
|
||||||
|
|
||||||
const index = numberOfExamples > 1 ? exampleIndex : undefined;
|
return <Box key={idx}>{comment.customBlocks.map((node, idx) => createNode(node, idx))}</Box>;
|
||||||
|
}
|
||||||
return (
|
default:
|
||||||
<BlockComment tagName={tag.tagName} key={idx} index={index}>
|
console.log(`Captured unknown node kind: ${node.kind}`);
|
||||||
{(node as DocBlockJSON).content.map((node, idx) => createNode(node, idx))}
|
break;
|
||||||
</BlockComment>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
case DocNodeKind.Comment: {
|
|
||||||
const comment = node as DocCommentJSON;
|
|
||||||
// Cheat a bit by finding out how many comments we have beforehand...
|
|
||||||
numberOfExamples = comment.customBlocks.filter(
|
|
||||||
(block) => block.tag.tagName.toUpperCase() === StandardTags.example.tagNameWithUpperCase,
|
|
||||||
).length;
|
|
||||||
|
|
||||||
return <Box key={idx}>{comment.customBlocks.map((node, idx) => createNode(node, idx))}</Box>;
|
return null;
|
||||||
}
|
},
|
||||||
default:
|
[colorScheme],
|
||||||
console.log(`Captured unknown node kind: ${node.kind}`);
|
);
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
|
|||||||
Reference in New Issue
Block a user