refactor: docs design (#8487)

This commit is contained in:
Noel
2022-08-15 14:48:00 +02:00
committed by GitHub
parent d09ef1e425
commit 4ab1d09997
44 changed files with 1533 additions and 1251 deletions

View File

@@ -6,8 +6,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format" "fmt": "yarn format"
}, },
"main": "./dist/index.mjs", "main": "./dist/index.mjs",

View File

@@ -5,8 +5,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local", "docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn lint && yarn test && yarn build", "prepack": "yarn lint && yarn test && yarn build",

View File

@@ -5,8 +5,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local", "docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn lint && yarn test && yarn build", "prepack": "yarn lint && yarn test && yarn build",

View File

@@ -4,8 +4,8 @@
"description": "The docs.json generator for discord.js and its related projects", "description": "The docs.json generator for discord.js and its related projects",
"scripts": { "scripts": {
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src --ext mjs,js,ts",
"format": "prettier --write . && eslint src --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"prepack": "yarn format && yarn build", "prepack": "yarn format && yarn build",
"changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/docgen/*'", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/docgen/*'",
@@ -50,7 +50,7 @@
"devDependencies": { "devDependencies": {
"@favware/cliff-jumper": "^1.8.6", "@favware/cliff-jumper": "^1.8.6",
"@types/jsdoc-to-markdown": "^7.0.3", "@types/jsdoc-to-markdown": "^7.0.3",
"@types/node": "^16.11.47", "@types/node": "^16.11.48",
"@typescript-eslint/eslint-plugin": "^5.33.0", "@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0", "@typescript-eslint/parser": "^5.33.0",
"eslint": "^8.22.0", "eslint": "^8.22.0",
@@ -61,7 +61,7 @@
"prettier": "^2.7.1", "prettier": "^2.7.1",
"rollup-plugin-typescript2": "0.32.1", "rollup-plugin-typescript2": "0.32.1",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"unbuild": "^0.8.4" "unbuild": "^0.8.8"
}, },
"engines": { "engines": {
"node": ">=16.9.0" "node": ">=16.9.0"

View File

@@ -4,8 +4,8 @@
"description": "Lightweight HTTP proxy for Discord's API, brought to you as a container 📦", "description": "Lightweight HTTP proxy for Discord's API, brought to you as a container 📦",
"scripts": { "scripts": {
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src --ext mjs,js,ts",
"format": "prettier --write . && eslint src --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"prepack": "yarn lint && yarn test && yarn build", "prepack": "yarn lint && yarn test && yarn build",
"changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/proxy-container/*'" "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/proxy-container/*'"

View File

@@ -5,8 +5,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local", "docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn lint && yarn test && yarn build", "prepack": "yarn lint && yarn test && yarn build",

View File

@@ -5,8 +5,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local", "docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn lint && yarn test && yarn build", "prepack": "yarn lint && yarn test && yarn build",

View File

@@ -6,8 +6,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src --ext mjs,js,ts",
"format": "prettier --write . && eslint src --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src --ext mjs,js,ts --fix",
"fmt": "yarn format" "fmt": "yarn format"
}, },
"main": "./dist/index.cjs", "main": "./dist/index.cjs",

View File

@@ -5,8 +5,8 @@
"scripts": { "scripts": {
"build": "unbuild", "build": "unbuild",
"test": "jest --coverage", "test": "jest --coverage",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"fmt": "yarn format", "fmt": "yarn format",
"docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local", "docs": "downlevel-dts . docs --to=3.7 && docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn lint && yarn test && yarn build", "prepack": "yarn lint && yarn test && yarn build",

View File

@@ -12,8 +12,8 @@
"dev:next": "next dev", "dev:next": "next dev",
"dev:css": "yarn generate:css --watch", "dev:css": "yarn generate:css --watch",
"generate:css": "unocss 'src/**/*.tsx' --out-file ./src/styles/unocss.css", "generate:css": "unocss 'src/**/*.tsx' --out-file ./src/styles/unocss.css",
"lint": "prettier --check . && eslint src --ext mjs,js,ts,tsx", "lint": "prettier --check . && TIMING=1 eslint src --ext mjs,js,ts,tsx",
"format": "prettier --write . && eslint src --ext mjs,js,ts,tsx --fix" "format": "prettier --write . && TIMING=1 eslint src --ext mjs,js,ts,tsx --fix"
}, },
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.mjs", "module": "./dist/index.mjs",
@@ -47,11 +47,17 @@
}, },
"homepage": "https://discord.js.org", "homepage": "https://discord.js.org",
"dependencies": { "dependencies": {
"@emotion/react": "^11.10.0",
"@emotion/server": "^11.10.0",
"@mantine/core": "^5.1.6",
"@mantine/hooks": "^5.1.6",
"@mantine/next": "^5.1.6",
"@mantine/nprogress": "^5.1.6",
"@mantine/spotlight": "^5.1.6",
"@microsoft/api-extractor-model": "^7.23.0", "@microsoft/api-extractor-model": "^7.23.0",
"@microsoft/tsdoc": "0.14.1", "@microsoft/tsdoc": "0.14.1",
"@microsoft/tsdoc-config": "0.16.1", "@microsoft/tsdoc-config": "0.16.1",
"@vscode/codicons": "^0.0.32", "@vscode/codicons": "^0.0.32",
"framer-motion": "^7.1.0",
"next": "^12.2.5", "next": "^12.2.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
@@ -60,9 +66,6 @@
"sharp": "^0.30.7" "sharp": "^0.30.7"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/cypress": "^8.0.3",
"@testing-library/dom": "^8.17.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.3.0", "@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.4.3",
"@types/node": "^16.11.48", "@types/node": "^16.11.48",
@@ -85,7 +88,6 @@
"eslint-plugin-react": "^7.30.1", "eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-hooks": "^4.6.0",
"happy-dom": "^6.0.4", "happy-dom": "^6.0.4",
"msw": "^0.44.2",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"typescript": "^4.7.4", "typescript": "^4.7.4",
"unocss": "^0.45.6", "unocss": "^0.45.6",

View File

@@ -1,3 +1,4 @@
import { Group, Stack, Title } from '@mantine/core';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { CommentSection } from './Comment'; import { CommentSection } from './Comment';
import { HyperlinkedText } from './HyperlinkedText'; import { HyperlinkedText } from './HyperlinkedText';
@@ -9,36 +10,33 @@ export enum CodeListingSeparatorType {
Value = '=', Value = '=',
} }
export interface CodeListingProps { export function CodeListing({
name,
separator = CodeListingSeparatorType.Type,
summary,
typeTokens,
children,
}: {
name: string; name: string;
summary?: ReturnType<DocItem['toJSON']>['summary']; summary?: ReturnType<DocItem['toJSON']>['summary'];
typeTokens: TokenDocumentation[]; typeTokens: TokenDocumentation[];
separator?: CodeListingSeparatorType; separator?: CodeListingSeparatorType;
children?: ReactNode; children?: ReactNode;
className?: string | undefined; className?: string | undefined;
} }) {
export function CodeListing({
name,
className,
separator = CodeListingSeparatorType.Type,
summary,
typeTokens,
children,
}: CodeListingProps) {
return ( return (
<div className={className}> <Stack key={name}>
<div key={name} className="flex flex-col"> <Group>
<div className="w-full flex flex-row gap-3"> <Title order={4} className="font-mono">
<h4 className="font-mono m-0">{`${name}`}</h4> {name}
<h4 className="m-0">{separator}</h4> </Title>
<h4 className="font-mono m-0 break-all"> <Title order={4}>{separator}</Title>
<HyperlinkedText tokens={typeTokens} /> <Title order={4} className="font-mono break-all">
</h4> <HyperlinkedText tokens={typeTokens} />
</div> </Title>
{summary && <CommentSection textClassName="text-dark-100 dark:text-gray-300" node={summary} />} </Group>
{children} {summary && <CommentSection node={summary} />}
</div> {children}
</div> </Stack>
); );
} }

View File

@@ -1,27 +1,28 @@
import { Anchor, Box, Text } from '@mantine/core';
import Link from 'next/link'; import Link from 'next/link';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter'; import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import type { CommentNode } from '~/DocModel/comment/CommentNode'; import type { CommentNode } from '~/DocModel/comment/CommentNode';
import type { CommentNodeContainer } from '~/DocModel/comment/CommentNodeContainer'; import type { CommentNodeContainer } from '~/DocModel/comment/CommentNodeContainer';
import type { FencedCodeCommentNode } from '~/DocModel/comment/FencedCodeCommentNode'; import type { FencedCodeCommentNode } from '~/DocModel/comment/FencedCodeCommentNode';
import type { LinkTagCommentNode } from '~/DocModel/comment/LinkTagCommentNode'; import type { LinkTagCommentNode } from '~/DocModel/comment/LinkTagCommentNode';
import type { PlainTextCommentNode } from '~/DocModel/comment/PlainTextCommentNode'; import type { PlainTextCommentNode } from '~/DocModel/comment/PlainTextCommentNode';
export interface RemarksBlockProps { export function CommentSection({ node }: { node: ReturnType<CommentNode['toJSON']> }): JSX.Element {
node: ReturnType<CommentNode['toJSON']>;
textClassName?: string | undefined;
}
export function CommentSection({ node, textClassName }: RemarksBlockProps): JSX.Element {
const createNode = (node: ReturnType<CommentNode['toJSON']>, idx?: number): ReactNode => { const createNode = (node: ReturnType<CommentNode['toJSON']>, idx?: number): ReactNode => {
switch (node.kind) { switch (node.kind) {
case 'PlainText': case 'PlainText':
return <span key={idx}>{(node as ReturnType<PlainTextCommentNode['toJSON']>).text}</span>; return (
<Text key={idx} span>
{(node as ReturnType<PlainTextCommentNode['toJSON']>).text}
</Text>
);
case 'Paragraph': case 'Paragraph':
return ( return (
<p key={idx} className={textClassName}> <Text key={idx} inline>
{(node as ReturnType<CommentNodeContainer['toJSON']>).nodes.map((node, idx) => createNode(node, idx))} {(node as ReturnType<CommentNodeContainer['toJSON']>).nodes.map((node, idx) => createNode(node, idx))}
</p> </Text>
); );
case 'SoftBreak': case 'SoftBreak':
return <br key={idx} />; return <br key={idx} />;
@@ -29,18 +30,41 @@ export function CommentSection({ node, textClassName }: RemarksBlockProps): JSX.
const { codeDestination, urlDestination, text } = node as ReturnType<LinkTagCommentNode['toJSON']>; const { codeDestination, urlDestination, text } = node as ReturnType<LinkTagCommentNode['toJSON']>;
if (codeDestination) { if (codeDestination) {
return <Link href={codeDestination.path}>{text ?? codeDestination.name}</Link>; return (
<Link key={idx} href={codeDestination.path} passHref>
<Anchor component="a" className="font-mono">
{text ?? codeDestination.name}
</Anchor>
</Link>
);
} }
if (urlDestination) { if (urlDestination) {
return <Link href={urlDestination}>{text ?? urlDestination}</Link>; return (
<Link key={idx} href={urlDestination} passHref>
<Anchor component="a" className="font-mono">
{text ?? urlDestination}
</Anchor>
</Link>
);
} }
return null; return null;
} }
case 'FencedCodeBlock': { case 'FencedCodeBlock': {
const { language, code } = node as ReturnType<FencedCodeCommentNode['toJSON']>; const { language, code } = node as ReturnType<FencedCodeCommentNode['toJSON']>;
return <SyntaxHighlighter language={language}>{code}</SyntaxHighlighter>; return (
<SyntaxHighlighter
key={idx}
wrapLines
wrapLongLines
language={language}
style={vscDarkPlus}
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
>
{code}
</SyntaxHighlighter>
);
} }
default: default:
break; break;
@@ -50,12 +74,12 @@ export function CommentSection({ node, textClassName }: RemarksBlockProps): JSX.
}; };
return ( return (
<div> <Box>
{node.kind === 'Paragraph' || node.kind === 'Section' ? ( {node.kind === 'Paragraph' || node.kind === 'Section' ? (
<>{(node as CommentNodeContainer).nodes.map((node, idx) => createNode(node, idx))}</> <>{(node as CommentNodeContainer).nodes.map((node, idx) => createNode(node, idx))}</>
) : ( ) : (
<>{createNode(node)}</> createNode(node)
)} )}
</div> </Box>
); );
} }

View File

@@ -1,3 +1,5 @@
import { Group, Stack, Title, Text, Box } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { VscListSelection, VscSymbolParameter } from 'react-icons/vsc'; import { VscListSelection, VscSymbolParameter } from 'react-icons/vsc';
import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter'; import { PrismAsyncLight as SyntaxHighlighter } from 'react-syntax-highlighter';
@@ -32,73 +34,73 @@ export function DocContainer({
extendsTokens, extendsTokens,
implementsTokens, implementsTokens,
}: DocContainerProps) { }: DocContainerProps) {
const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
return ( return (
<div className="flex flex-col min-h-full max-h-full grow"> <Stack>
<div className="border-0.5 border-gray px-10 py-2"> <Title order={2} ml="xs">
<h2 className="flex gap-2 items-center break-all m-0 dark:text-white"> <Group>
{generateIcon(kind)} {generateIcon(kind)}
{name} {name}
</h2> </Group>
</div> </Title>
<div className="min-h-full overflow-y-auto overflow-x-clip px-10 pt-5 pb-10"> <Section title="Summary" icon={<VscListSelection />} padded dense={matches}>
<Section iconElement={<VscListSelection />} title="Summary" className="dark:text-white mb-5"> {summary ? <CommentSection node={summary} /> : <Text>No summary provided.</Text>}
{summary ? ( </Section>
<CommentSection textClassName="text-dark-100 dark:text-gray-300" node={summary} />
) : ( <Box px="xs" pb="xs">
<p className="text-dark-100 dark:text-gray-300">No summary provided.</p> <SyntaxHighlighter
)} wrapLongLines
</Section> language="typescript"
<div className={extendsTokens?.length ? 'mb-2' : 'mb-10'}> style={vscDarkPlus}
<SyntaxHighlighter codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
wrapLines >
wrapLongLines {excerpt}
language="typescript" </SyntaxHighlighter>
style={vscDarkPlus} </Box>
codeTagProps={{ style: { fontFamily: 'JetBrains Mono' } }}
> {extendsTokens?.length ? (
{excerpt} <Group noWrap>
</SyntaxHighlighter> <Title order={3} ml="xs">
</div> Extends
{extendsTokens?.length ? ( </Title>
<div <Title order={3} ml="xs">
className={`flex flex-row items-center dark:text-white gap-3 ${implementsTokens?.length ? '' : 'mb-10'}`} {CodeListingSeparatorType.Type}
> </Title>
<h3 className="m-0">Extends</h3> <Text className="font-mono break-all">
<h3 className="m-0">{CodeListingSeparatorType.Type}</h3> <HyperlinkedText tokens={extendsTokens} />
<p className="font-mono break-all"> </Text>
<HyperlinkedText tokens={extendsTokens} /> </Group>
</p> ) : null}
</div>
{implementsTokens?.length ? (
<Group noWrap>
<Title order={3} ml="xs">
Implements
</Title>
<Title order={3} ml="xs">
{CodeListingSeparatorType.Type}
</Title>
<Text className="font-mono break-all">
{implementsTokens.map((token, idx) => (
<>
<HyperlinkedText tokens={token} />
{idx < implementsTokens.length - 1 ? ', ' : ''}
</>
))}
</Text>
</Group>
) : null}
<Stack>
{typeParams?.length ? (
<Section title="Type Parameters" icon={<VscSymbolParameter />} padded dense={matches} defaultClosed>
<TypeParamTable data={typeParams} />
</Section>
) : null} ) : null}
{implementsTokens?.length ? ( <Stack>{children}</Stack>
<div className={`flex flex-row items-center dark:text-white gap-3 mb-10`}> </Stack>
<h3 className="m-0">Implements</h3> </Stack>
<h3 className="m-0">{CodeListingSeparatorType.Type}</h3>
<p className="font-mono break-all">
{implementsTokens.map((token, i) => (
<>
<HyperlinkedText key={i} tokens={token} />
{i < implementsTokens.length - 1 ? ', ' : ''}
</>
))}
</p>
</div>
) : null}
<div className="space-y-10">
{typeParams?.length ? (
<Section
iconElement={<VscSymbolParameter />}
title="Type Parameters"
className="dark:text-white"
defaultClosed
>
<TypeParamTable data={typeParams} />
</Section>
) : null}
<div className="space-y-10">{children}</div>
</div>
</div>
</div>
); );
} }

View File

@@ -1,10 +1,7 @@
import { Anchor, Text } from '@mantine/core';
import Link from 'next/link'; import Link from 'next/link';
import type { TokenDocumentation } from '~/util/parse.server'; import type { TokenDocumentation } from '~/util/parse.server';
export interface HyperlinkedTextProps {
tokens: TokenDocumentation[];
}
/** /**
* Constructs a hyperlinked html node based on token type references * Constructs a hyperlinked html node based on token type references
* *
@@ -12,22 +9,24 @@ export interface HyperlinkedTextProps {
* *
* @returns An array of JSX elements and string comprising the hyperlinked text * @returns An array of JSX elements and string comprising the hyperlinked text
*/ */
export function HyperlinkedText({ tokens }: HyperlinkedTextProps) { export function HyperlinkedText({ tokens }: { tokens: TokenDocumentation[] }) {
return ( return (
<> <>
{tokens.map((token) => { {tokens.map((token, idx) => {
if (token.path) { if (token.path) {
return ( return (
<Link key={token.text} href={token.path}> <Link key={idx} href={token.path} passHref>
<a className="text-blue-500 dark:text-blue-300 no-underline">{token.text}</a> <Anchor component="a" inherit>
{token.text}
</Anchor>
</Link> </Link>
); );
} }
return ( return (
<span key={token.text} className="text-blue-500 dark:text-blue-300"> <Text key={idx} span unstyled>
{token.text} {token.text}
</span> </Text>
); );
})} })}
</> </>

View File

@@ -1,43 +0,0 @@
import { FiMenu } from 'react-icons/fi';
import { VscPackage } from 'react-icons/vsc';
import { ListSidebar } from './ListSidebar';
import type { DocItem } from '~/DocModel/DocItem';
import type { getMembers } from '~/util/parse.server';
export interface ItemListProps {
packageName: string;
data: {
members: ReturnType<typeof getMembers>;
};
selectedMember?: ReturnType<DocItem['toJSON']> | undefined;
}
function onMenuClick() {
console.log('menu clicked');
// Todo show/hide list
}
export function ItemSidebar({ packageName, data, selectedMember }: ItemListProps) {
return (
<div className="flex flex-col min-h-full max-h-full grow min-w-[270px] lg:border-r-solid border-0.5 border-gray">
<div className="border-b-0.5 border-gray py-2">
<h2 className="flex gap-2 items-center m-0 px-2 dark:text-white">
<VscPackage />
{`${packageName}`}
</h2>
<button
type="button"
className="lg:hidden mr-2 bg-transparent border-none cursor-pointer"
title="Menu"
onClick={onMenuClick}
>
<FiMenu size={32} />
</button>
</div>
<div className="hidden lg:block lg:min-h-full overflow-y-auto overflow-x-clip py-3 px-4">
<ListSidebar members={data.members} title="test" selectedMember={selectedMember} />
</div>
</div>
);
}

View File

@@ -1,4 +1,4 @@
import { FiLink } from 'react-icons/fi'; import { Group, Stack, Title } from '@mantine/core';
import { CommentSection } from './Comment'; import { CommentSection } from './Comment';
import { HyperlinkedText } from './HyperlinkedText'; import { HyperlinkedText } from './HyperlinkedText';
import { ParameterTable } from './ParameterTable'; import { ParameterTable } from './ParameterTable';
@@ -7,10 +7,6 @@ import type { DocMethodSignature } from '~/DocModel/DocMethodSignature';
type MethodResolvable = ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>; type MethodResolvable = ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>;
export interface MethodItemProps {
data: MethodResolvable;
}
function getShorthandName(data: MethodResolvable) { function getShorthandName(data: MethodResolvable) {
return `${data.name}(${data.parameters.reduce((prev, cur, index) => { return `${data.name}(${data.parameters.reduce((prev, cur, index) => {
if (index === 0) { if (index === 0) {
@@ -21,37 +17,24 @@ function getShorthandName(data: MethodResolvable) {
}, '')})`; }, '')})`;
} }
function onAnchorClick() { export function MethodItem({ data }: { data: MethodResolvable }) {
console.log('anchor clicked');
// Todo implement jump-to links
}
export function MethodItem({ data }: MethodItemProps) {
return ( return (
<div className="flex flex-col"> <Stack>
<div className="flex"> <Group>
<button <Stack>
type="button" <Group>
className="bg-transparent border-none cursor-pointer dark:text-white" <Title order={5} className="font-mono break-all">{`${getShorthandName(data)}`}</Title>
title="Anchor" <Title order={5}>:</Title>
onClick={onAnchorClick} <Title order={5} className="font-mono break-all">
>
<FiLink size={16} />
</button>
<div className="flex flex-col">
<div className="w-full flex flex-row gap-3">
<h4 className="font-mono m-0 break-all">{`${getShorthandName(data)}`}</h4>
<h4 className="m-0">:</h4>
<h4 className="font-mono m-0 break-all">
<HyperlinkedText tokens={data.returnTypeTokens} /> <HyperlinkedText tokens={data.returnTypeTokens} />
</h4> </Title>
</div> </Group>
</div> </Stack>
</div> </Group>
<div className="mx-7 mb-5"> <Group sx={{ display: data.summary || data.parameters.length ? 'block' : 'none' }} mb="lg">
{data.summary && <CommentSection textClassName="text-dark-100 dark:text-gray-300" node={data.summary} />} {data.summary ? <CommentSection node={data.summary} /> : null}
{data.parameters.length ? <ParameterTable data={data.parameters} /> : null} {data.parameters.length ? <ParameterTable data={data.parameters} /> : null}
</div> </Group>
</div> </Stack>
); );
} }

View File

@@ -1,19 +1,18 @@
import { Stack } from '@mantine/core';
import { MethodItem } from './MethodItem'; import { MethodItem } from './MethodItem';
import type { DocMethod } from '~/DocModel/DocMethod'; import type { DocMethod } from '~/DocModel/DocMethod';
import type { DocMethodSignature } from '~/DocModel/DocMethodSignature'; import type { DocMethodSignature } from '~/DocModel/DocMethodSignature';
export interface MethodListProps { export function MethodList({
data,
}: {
data: (ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>)[]; data: (ReturnType<DocMethod['toJSON']> | ReturnType<DocMethodSignature['toJSON']>)[];
} }) {
export function MethodList({ data }: MethodListProps) {
return ( return (
<div> <Stack>
<div className="flex flex-col gap-5"> {data.map((method) => (
{data.map((method) => ( <MethodItem key={method.name} data={method} />
<MethodItem key={method.name} data={method} /> ))}
))} </Stack>
</div>
</div>
); );
} }

View File

@@ -2,12 +2,7 @@ import { HyperlinkedText } from './HyperlinkedText';
import { Table } from './Table'; import { Table } from './Table';
import type { ParameterDocumentation } from '~/util/parse.server'; import type { ParameterDocumentation } from '~/util/parse.server';
interface ParameterDetailProps { export function ParameterTable({ data }: { data: ParameterDocumentation[] }) {
data: ParameterDocumentation[];
className?: string | undefined;
}
export function ParameterTable({ data, className }: ParameterDetailProps) {
const rows = data.map((param) => ({ const rows = data.map((param) => ({
Name: param.name, Name: param.name,
Type: <HyperlinkedText tokens={param.tokens} />, Type: <HyperlinkedText tokens={param.tokens} />,
@@ -20,12 +15,5 @@ export function ParameterTable({ data, className }: ParameterDetailProps) {
Type: 'font-mono', Type: 'font-mono',
}; };
return ( return <Table columns={['Name', 'Type', 'Optional', 'Description']} rows={rows} columnStyles={columnStyles} />;
<Table
className={className}
columns={['Name', 'Type', 'Optional', 'Description']}
rows={rows}
columnStyles={columnStyles}
/>
);
} }

View File

@@ -1,16 +1,13 @@
import { Stack } from '@mantine/core';
import { CodeListing } from './CodeListing'; import { CodeListing } from './CodeListing';
import type { DocProperty } from '~/DocModel/DocProperty'; import type { DocProperty } from '~/DocModel/DocProperty';
export interface PropertyListProps { export function PropertyList({ data }: { data: ReturnType<DocProperty['toJSON']>[] }) {
data: ReturnType<DocProperty['toJSON']>[];
}
export function PropertyList({ data }: PropertyListProps) {
return ( return (
<div className="flex flex-col gap-5"> <Stack>
{data.map((prop) => ( {data.map((prop) => (
<CodeListing key={prop.name} name={prop.name} typeTokens={prop.propertyTypeTokens} summary={prop.summary} /> <CodeListing key={prop.name} name={prop.name} typeTokens={prop.propertyTypeTokens} summary={prop.summary} />
))} ))}
</div> </Stack>
); );
} }

View File

@@ -0,0 +1,24 @@
import { startNavigationProgress, resetNavigationProgress, NavigationProgress } from '@mantine/nprogress';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
export function RouterTransition() {
const router = useRouter();
useEffect(() => {
const handleStart = (url: string) => url !== router.asPath && startNavigationProgress();
const handleComplete = () => resetNavigationProgress();
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeComplete', handleComplete);
router.events.on('routeChangeError', handleComplete);
return () => {
router.events.off('routeChangeStart', handleStart);
router.events.off('routeChangeComplete', handleComplete);
router.events.off('routeChangeError', handleComplete);
};
}, [router.asPath]);
return <NavigationProgress />;
}

View File

@@ -1,72 +1,71 @@
import { AnimatePresence, motion } from 'framer-motion'; import { createStyles, UnstyledButton, Group, ThemeIcon, Collapse, Box, Text } from '@mantine/core';
import { type ReactNode, useState } from 'react'; import { type ReactNode, useState } from 'react';
import { VscChevronDown, VscChevronRight } from 'react-icons/vsc'; import { VscChevronDown } from 'react-icons/vsc';
import { Separator } from './Seperator';
export interface SectionProps { const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({
children: ReactNode; control: {
title: string; display: 'block',
className?: string | undefined; width: '100%',
defaultClosed?: boolean; padding: `${theme.spacing.xs}px ${theme.spacing.xs}px`,
iconElement?: JSX.Element; color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
showSeparator?: boolean; fontSize: theme.fontSizes.sm,
margin?: boolean;
} '&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
},
},
icon: {
transition: 'transform 150ms ease',
transform: opened ? 'rotate(180deg)' : 'rotate(0deg)',
},
}));
export function Section({ export function Section({
title, title,
icon,
padded = false,
dense = false,
defaultClosed = false,
children, children,
className, }: {
defaultClosed, title: string;
iconElement, icon?: JSX.Element;
showSeparator = true, padded?: boolean;
margin = true, dense?: boolean;
}: SectionProps) { defaultClosed?: boolean;
const [collapsed, setCollapsed] = useState(defaultClosed ?? false); children: ReactNode;
}) {
const [opened, setOpened] = useState(!defaultClosed);
const { classes } = useStyles({ opened });
return ( return (
<div className={className}> <Box className="break-all">
<h3 <UnstyledButton className={classes.control} onClick={() => setOpened((o) => !o)}>
className="flex gap-2 whitespace-pre-wrap font-semibold dark:text-white cursor-pointer" <Group position="apart">
onClick={() => setCollapsed(!collapsed)} <Group>
> {icon ? (
{collapsed ? <VscChevronRight size={20} /> : <VscChevronDown size={20} />} <ThemeIcon variant="outline" size={30}>
{iconElement ?? null} {icon}
{title} </ThemeIcon>
</h3> ) : null}
<AnimatePresence initial={false} exitBeforeEnter> <Text weight={600} size="md">
{collapsed ? null : ( {title}
<> </Text>
<motion.div </Group>
transition={{ duration: 0.5, ease: [0.04, 0.62, 0.23, 0.98] }} <VscChevronDown size={20} className={classes.icon} />
key="content" </Group>
initial="collapsed" </UnstyledButton>
animate="open" <Collapse in={opened}>
exit="collapsed" {padded ? (
variants={{ <Box py={20} px={dense ? 0 : 31} mx={dense ? 10 : 25}>
open: { {children}
opacity: 1, </Box>
height: 'auto', ) : (
paddingLeft: '1.75rem', children
paddingRight: '1.75rem',
marginBottom: margin ? '1.25rem' : 0,
},
collapsed: {
opacity: 0,
height: 0,
paddingLeft: '1.75rem',
paddingRight: '1.75rem',
paddingBottom: 0,
marginBottom: 0,
},
}}
>
{children}
</motion.div>
{showSeparator && <Separator />}
</>
)} )}
</AnimatePresence> </Collapse>
</div> </Box>
); );
} }

View File

@@ -1,3 +1,4 @@
import { useMediaQuery } from '@mantine/hooks';
import { VscSymbolConstant, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc'; import { VscSymbolConstant, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
import { MethodList } from './MethodList'; import { MethodList } from './MethodList';
import { ParameterTable } from './ParameterTable'; import { ParameterTable } from './ParameterTable';
@@ -6,37 +7,31 @@ import { Section } from './Section';
import type { DocInterface } from '~/DocModel/DocInterface'; import type { DocInterface } from '~/DocModel/DocInterface';
import type { ParameterDocumentation } from '~/util/parse.server'; import type { ParameterDocumentation } from '~/util/parse.server';
export interface PropertiesSectionProps { export function PropertiesSection({ data }: { data: ReturnType<DocInterface['toJSON']>['properties'] }) {
data: ReturnType<DocInterface['toJSON']>['properties']; const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
}
export function PropertiesSection({ data }: PropertiesSectionProps) {
return data.length ? ( return data.length ? (
<Section iconElement={<VscSymbolProperty />} title="Properties" className="dark:text-white"> <Section title="Properties" icon={<VscSymbolProperty />} padded dense={matches}>
<PropertyList data={data} /> <PropertyList data={data} />
</Section> </Section>
) : null; ) : null;
} }
export interface MethodsSectionProps { export function MethodsSection({ data }: { data: ReturnType<DocInterface['toJSON']>['methods'] }) {
data: ReturnType<DocInterface['toJSON']>['methods']; const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
}
export function MethodsSection({ data }: MethodsSectionProps) {
return data.length ? ( return data.length ? (
<Section iconElement={<VscSymbolMethod />} title="Methods" className="dark:text-white"> <Section title="Methods" icon={<VscSymbolMethod />} padded dense={matches}>
<MethodList data={data} /> <MethodList data={data} />
</Section> </Section>
) : null; ) : null;
} }
export interface ParametersSectionProps { export function ParametersSection({ data }: { data: ParameterDocumentation[] }) {
data: ParameterDocumentation[]; const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
}
export function ParametersSection({ data }: ParametersSectionProps) {
return data.length ? ( return data.length ? (
<Section iconElement={<VscSymbolConstant />} title="Parameters" className="dark:text-white"> <Section title="Parameters" icon={<VscSymbolConstant />} padded dense={matches}>
<ParameterTable data={data} /> <ParameterTable data={data} />
</Section> </Section>
) : null; ) : null;

View File

@@ -1,3 +0,0 @@
export function Separator() {
return <div className="h-[1px] w-full bg-gray-300" />;
}

View File

@@ -1,32 +1,16 @@
import { createStyles, UnstyledButton, Group, Text } from '@mantine/core';
import Link from 'next/link'; import Link from 'next/link';
import type { Dispatch, SetStateAction } from 'react';
import { import {
VscSymbolClass, VscSymbolClass,
VscSymbolEnum, VscSymbolEnum,
VscSymbolField,
VscSymbolInterface, VscSymbolInterface,
VscSymbolMethod, VscSymbolField,
VscSymbolVariable, VscSymbolVariable,
VscSymbolMethod,
} from 'react-icons/vsc'; } from 'react-icons/vsc';
import type { ItemListProps } from './ItemSidebar';
import { Section } from './Section'; import { Section } from './Section';
import type { DocItem } from '~/DocModel/DocItem'; import type { GroupedMembers, Members } from './SidebarLayout';
export type Members = ItemListProps['data']['members'];
export interface ListSidebarSectionProps {
members: Members;
selectedMember?: ReturnType<DocItem['toJSON']> | undefined;
title: string;
}
interface GroupedMembers {
Classes: Members;
Functions: Members;
Enums: Members;
Interfaces: Members;
Types: Members;
Variables: Members;
}
function groupMembers(members: Members): GroupedMembers { function groupMembers(members: Members): GroupedMembers {
const Classes: Members = []; const Classes: Members = [];
@@ -81,42 +65,54 @@ function resolveIcon(item: keyof GroupedMembers) {
} }
} }
export function ListSidebar({ members, selectedMember }: ListSidebarSectionProps) { const useStyles = createStyles((theme) => ({
link: {
fontWeight: 500,
display: 'block',
padding: 5,
paddingLeft: 31,
marginLeft: 25,
fontSize: theme.fontSizes.sm,
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.colors.gray![7],
borderLeft: `1px solid ${theme.colorScheme === 'dark' ? theme.colors.dark![4] : theme.colors.gray![3]}`,
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
},
},
}));
export function SidebarItems({
members,
setOpened,
}: {
members: Members;
setOpened: Dispatch<SetStateAction<boolean>>;
}) {
const { classes } = useStyles();
const groupItems = groupMembers(members); const groupItems = groupMembers(members);
return ( return (
<> <>
{(Object.keys(groupItems) as (keyof GroupedMembers)[]) {(Object.keys(groupItems) as (keyof GroupedMembers)[])
.filter((group) => groupItems[group].length) .filter((group) => groupItems[group].length)
.map((group, i) => ( .map((group, idx) => (
<Section iconElement={resolveIcon(group)} key={i} title={group} showSeparator={false}> <Section key={idx} title={group} icon={resolveIcon(group)}>
<div className="space-y-2"> {groupItems[group].map((member, i) => (
{groupItems[group].map((member, i) => ( <Link key={i} href={member.path} passHref>
<div <UnstyledButton className={classes.link} component="a" onClick={() => setOpened((o) => !o)}>
key={i} <Group>
className="flex gap-2 whitespace-pre-wrap no-underline break-all justify-between text-blue-500 dark:text-blue-300" <Text>{member.name}</Text>
>
<Link href={member.path}>
<a
className={`no-underline m-0 text-sm font-semibold ${
selectedMember?.containerKey === member.containerKey
? 'text-blue-500 dark:text-blue-300'
: 'text-gray-500 dark:text-gray-300 hover:text-dark-100 dark:hover:text-white'
}`}
>
{member.name}
</a>
</Link>
<div>
{member.overloadIndex && member.overloadIndex > 1 ? ( {member.overloadIndex && member.overloadIndex > 1 ? (
<div className="flex font-mono w-[15px] h-[15px] items-center justify-center rounded-md border border-2 font-bold text-sm color-gray-500 dark:color-gray-300"> <Text size="xs" color="dimmed">
<p className="font-semibold">{`${member.overloadIndex}`}</p> {member.overloadIndex}
</div> </Text>
) : null} ) : null}
</div> </Group>
</div> </UnstyledButton>
))} </Link>
</div> ))}
</Section> </Section>
))} ))}
</> </>

View File

@@ -1,30 +1,207 @@
import type { PropsWithChildren } from 'react'; import {
import { type ItemListProps, ItemSidebar } from './ItemSidebar'; useMantineTheme,
AppShell,
Navbar,
MediaQuery,
// Aside,
Header,
Burger,
Anchor,
Breadcrumbs,
ScrollArea,
Group,
Text,
ThemeIcon,
Box,
UnstyledButton,
createStyles,
Menu,
ActionIcon,
useMantineColorScheme,
} from '@mantine/core';
import { NextLink } from '@mantine/next';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { type PropsWithChildren, useState } from 'react';
import { VscChevronDown, VscPackage } from 'react-icons/vsc';
import { WiDaySunny, WiNightClear } from 'react-icons/wi';
import { SidebarItems } from './SidebarItems';
import type { DocItem } from '~/DocModel/DocItem';
import type { findMember } from '~/util/model.server'; import type { findMember } from '~/util/model.server';
import type { getMembers } from '~/util/parse.server';
export interface SidebarLayoutProps {
packageName: string;
data: {
members: ReturnType<typeof getMembers>;
member: ReturnType<typeof findMember>;
};
selectedMember?: ReturnType<DocItem['toJSON']> | undefined;
}
export type Members = SidebarLayoutProps['data']['members'];
export interface GroupedMembers {
Classes: Members;
Functions: Members;
Enums: Members;
Interfaces: Members;
Types: Members;
Variables: Members;
}
const useStyles = createStyles((theme, { opened }: { opened: boolean }) => ({
control: {
display: 'block',
width: '100%',
padding: theme.spacing.xs,
color: theme.colorScheme === 'dark' ? theme.colors.dark![0] : theme.black,
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark![7] : theme.colors.gray![0],
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
},
},
icon: {
transition: 'transform 150ms ease',
transform: opened ? 'rotate(180deg)' : 'rotate(0deg)',
},
}));
const libraries = [
{ label: 'builders', value: 'builders' },
{ label: 'collection', value: 'collection' },
{ label: 'discord.js', value: 'discord.js' },
{ label: 'proxy', value: 'proxy' },
{ label: 'rest', value: 'rest' },
{ label: 'voice', value: 'voice' },
{ label: 'ws', value: 'ws' },
];
export function SidebarLayout({ packageName, data, children }: PropsWithChildren<Partial<SidebarLayoutProps>>) {
const router = useRouter();
const theme = useMantineTheme();
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
const dark = colorScheme === 'dark';
const [opened, setOpened] = useState(false);
const [openedPicker, setOpenedPicker] = useState(false);
const { classes } = useStyles({ opened: openedPicker });
const libraryMenuItems = libraries.map((item) => (
<Menu.Item key={item.label} component={NextLink} href={`/docs/main/packages/${item.value}`}>
{item.label}
</Menu.Item>
));
const asPathWithoutQuery = router.asPath.split('?')[0];
const breadcrumbs = asPathWithoutQuery?.split('/').map((path, idx, original) => (
<Link key={idx} href={original.slice(0, idx + 1).join('/')} passHref>
<Anchor component="a">{path}</Anchor>
</Link>
));
export function SidebarLayout({
packageName,
data,
children,
}: PropsWithChildren<Partial<ItemListProps & { data: { member: ReturnType<typeof findMember> } }>>) {
return ( return (
<div className="flex flex-col max-w-full h-full max-h-full bg-white dark:bg-dark"> <AppShell
<div className="flex min-h-[40px] border-0.5 border-b-0 border-gray items-center m-0 px-2 dark:text-white"> styles={{
Breadcrumbs main: {
</div> background: theme.colorScheme === 'dark' ? theme.colors.dark![8] : theme.colors.gray![0],
<div className="flex flex-col lg:flex-row overflow-hidden"> },
<div className="h-full w-full lg:max-w-[310px] lg:min-w-[310px]"> }}
navbarOffsetBreakpoint="sm"
asideOffsetBreakpoint="sm"
navbar={
<Navbar hiddenBreakpoint="sm" hidden={!opened} width={{ sm: 200, lg: 300 }}>
{packageName && data ? ( {packageName && data ? (
<ItemSidebar packageName={packageName} data={data} selectedMember={data.member} /> <>
<Navbar.Section p="xs">
<>
<Menu
onOpen={() => setOpenedPicker(true)}
onClose={() => setOpenedPicker(false)}
radius="md"
width="target"
>
<Menu.Target>
<UnstyledButton className={classes.control}>
<Group position="apart">
<Group>
<ThemeIcon variant="outline" size={30}>
<VscPackage />
</ThemeIcon>
<Text weight="600" size="md">
{packageName}
</Text>
</Group>
<VscChevronDown className={classes.icon} size={20} />
</Group>
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>{libraryMenuItems}</Menu.Dropdown>
</Menu>
</>
</Navbar.Section>
<Navbar.Section p="xs" grow component={ScrollArea}>
<SidebarItems members={data.members} setOpened={setOpened} />
</Navbar.Section>
</>
) : null} ) : null}
</div> </Navbar>
<div className="h-full grow">{children}</div> }
<div className="h-full w-full lg:max-w-[310px] lg:min-w-[310px]"> // aside={
{packageName && data?.member ? ( // packageName && data?.member ? (
<ItemSidebar packageName={packageName} data={data} selectedMember={data.member} /> // <MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
) : null} // <Aside hiddenBreakpoint="sm" width={{ sm: 200, lg: 300 }}>
</div> // <ScrollArea p="xs">
</div> // <SidebarItems members={data.members} />
</div> // </ScrollArea>
// </Aside>
// </MediaQuery>
// ) : (
// <></>
// )
// }
// footer={
// <Footer height={60} p="md">
// Application footer
// </Footer>
// }
header={
<Header height={70} p="md">
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', height: '100%' }}>
<div>
<MediaQuery largerThan="sm" styles={{ display: 'none' }}>
<Burger
opened={opened}
onClick={() => setOpened((o) => !o)}
size="sm"
color={theme.colors.gray![6]}
mr="xl"
/>
</MediaQuery>
<MediaQuery smallerThan="sm" styles={{ display: 'none' }}>
<Breadcrumbs>{breadcrumbs}</Breadcrumbs>
</MediaQuery>
</div>
<ActionIcon
variant="outline"
color={dark ? 'yellow' : 'blurple'}
onClick={() => toggleColorScheme()}
title="Toggle color scheme"
>
{dark ? <WiDaySunny size={18} /> : <WiNightClear size={18} />}
</ActionIcon>
</Box>
</Header>
}
>
{children}
</AppShell>
); );
} }

View File

@@ -1,45 +1,37 @@
import { Table as MantineTable } from '@mantine/core';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
export interface RowData { export function Table({
monospace?: boolean; rows,
content: string; columns,
} columnStyles,
}: {
export interface TableProps {
columns: string[]; columns: string[];
columnStyles?: Record<string, string>; columnStyles?: Record<string, string>;
rows: Record<string, ReactNode>[]; rows: Record<string, ReactNode>[];
className?: string | undefined; }) {
}
export function Table({ rows, columns, columnStyles, className }: TableProps) {
return ( return (
<div className={className}> <MantineTable>
<table className="table-fixed w-full pb-10 border-collapse"> <thead>
<thead> <tr>
<tr> {columns.map((column) => (
{columns.map((column) => ( <th key={column} className="break-normal">
<th key={column} className="border-b z-10 text-left text-sm pl-2 border-gray"> {column}
{column} </th>
</th> ))}
</tr>
</thead>
<tbody>
{rows.map((row, idx) => (
<tr key={idx}>
{Object.entries(row).map(([colName, val]) => (
<td key={colName} className={columnStyles?.[colName] ?? ''}>
{val}
</td>
))} ))}
</tr> </tr>
</thead> ))}
<tbody> </tbody>
{rows.map((row, i) => ( </MantineTable>
<tr key={i}>
{Object.entries(row).map(([colName, val]) => (
<td
key={colName}
className={`p-2 text-sm border-b text-left border-gray break-all ${columnStyles?.[colName] ?? ''}`}
>
{val}
</td>
))}
</tr>
))}
</tbody>
</table>
</div>
); );
} }

View File

@@ -2,12 +2,7 @@ import { HyperlinkedText } from './HyperlinkedText';
import { Table } from './Table'; import { Table } from './Table';
import type { TypeParameterData } from '~/util/parse.server'; import type { TypeParameterData } from '~/util/parse.server';
export interface TableProps { export function TypeParamTable({ data }: { data: TypeParameterData[] }) {
data: TypeParameterData[];
className?: string;
}
export function TypeParamTable({ data, className }: TableProps) {
const rows = data.map((typeParam) => ({ const rows = data.map((typeParam) => ({
Name: typeParam.name, Name: typeParam.name,
Constraints: <HyperlinkedText tokens={typeParam.constraintTokens} />, Constraints: <HyperlinkedText tokens={typeParam.constraintTokens} />,
@@ -24,7 +19,6 @@ export function TypeParamTable({ data, className }: TableProps) {
return ( return (
<Table <Table
className={className}
columns={['Name', 'Constraints', 'Optional', 'Default', 'Description']} columns={['Name', 'Constraints', 'Optional', 'Default', 'Description']}
rows={rows} rows={rows}
columnStyles={rowElements} columnStyles={rowElements}

View File

@@ -2,11 +2,7 @@ import { DocContainer } from '../DocContainer';
import { MethodsSection, PropertiesSection } from '../Sections'; import { MethodsSection, PropertiesSection } from '../Sections';
import type { DocClass } from '~/DocModel/DocClass'; import type { DocClass } from '~/DocModel/DocClass';
export interface ClassProps { export function Class({ data }: { data: ReturnType<DocClass['toJSON']> }) {
data: ReturnType<DocClass['toJSON']>;
}
export function Class({ data }: ClassProps) {
return ( return (
<DocContainer <DocContainer
name={data.name} name={data.name}

View File

@@ -1,18 +1,18 @@
import { Stack } from '@mantine/core';
import { useMediaQuery } from '@mantine/hooks';
import { VscSymbolEnumMember } from 'react-icons/vsc'; import { VscSymbolEnumMember } from 'react-icons/vsc';
import { CodeListing, CodeListingSeparatorType } from '../CodeListing'; import { CodeListing, CodeListingSeparatorType } from '../CodeListing';
import { DocContainer } from '../DocContainer'; import { DocContainer } from '../DocContainer';
import { Section } from '../Section'; import { Section } from '../Section';
import type { DocEnum } from '~/DocModel/DocEnum'; import type { DocEnum } from '~/DocModel/DocEnum';
export interface EnumProps { export function Enum({ data }: { data: ReturnType<DocEnum['toJSON']> }) {
data: ReturnType<DocEnum['toJSON']>; const matches = useMediaQuery('(max-width: 768px)', true, { getInitialValueInEffect: false });
}
export function Enum({ data }: EnumProps) {
return ( return (
<DocContainer name={data.name} kind={data.kind} excerpt={data.excerpt} summary={data.summary}> <DocContainer name={data.name} kind={data.kind} excerpt={data.excerpt} summary={data.summary}>
<Section iconElement={<VscSymbolEnumMember />} title="Members" className="dark:text-white"> <Section title="Members" icon={<VscSymbolEnumMember />} padded dense={matches}>
<div className="flex flex-col gap-5"> <Stack>
{data.members.map((member) => ( {data.members.map((member) => (
<CodeListing <CodeListing
key={member.name} key={member.name}
@@ -22,7 +22,7 @@ export function Enum({ data }: EnumProps) {
summary={member.summary} summary={member.summary}
/> />
))} ))}
</div> </Stack>
</Section> </Section>
</DocContainer> </DocContainer>
); );

View File

@@ -2,11 +2,7 @@ import { DocContainer } from '../DocContainer';
import { ParametersSection } from '../Sections'; import { ParametersSection } from '../Sections';
import type { DocFunction } from '~/DocModel/DocFunction'; import type { DocFunction } from '~/DocModel/DocFunction';
export interface FunctionProps { export function Function({ data }: { data: ReturnType<DocFunction['toJSON']> }) {
data: ReturnType<DocFunction['toJSON']>;
}
export function Function({ data }: FunctionProps) {
return ( return (
<DocContainer <DocContainer
name={`${data.name}${data.overloadIndex ? ` (${data.overloadIndex})` : ''}`} name={`${data.name}${data.overloadIndex ? ` (${data.overloadIndex})` : ''}`}

View File

@@ -2,11 +2,7 @@ import { DocContainer } from '../DocContainer';
import { MethodsSection, PropertiesSection } from '../Sections'; import { MethodsSection, PropertiesSection } from '../Sections';
import type { DocInterface } from '~/DocModel/DocInterface'; import type { DocInterface } from '~/DocModel/DocInterface';
export interface InterfaceProps { export function Interface({ data }: { data: ReturnType<DocInterface['toJSON']> }) {
data: ReturnType<DocInterface['toJSON']>;
}
export function Interface({ data }: InterfaceProps) {
return ( return (
<DocContainer <DocContainer
name={data.name} name={data.name}

View File

@@ -1,11 +1,7 @@
import { DocContainer } from '../DocContainer'; import { DocContainer } from '../DocContainer';
import type { DocTypeAlias } from '~/DocModel/DocTypeAlias'; import type { DocTypeAlias } from '~/DocModel/DocTypeAlias';
export interface TypeAliasProps { export function TypeAlias({ data }: { data: ReturnType<DocTypeAlias['toJSON']> }) {
data: ReturnType<DocTypeAlias['toJSON']>;
}
export function TypeAlias({ data }: TypeAliasProps) {
return ( return (
<DocContainer <DocContainer
name={data.name} name={data.name}

View File

@@ -1,10 +1,6 @@
import { DocContainer } from '../DocContainer'; import { DocContainer } from '../DocContainer';
import type { DocVariable } from '~/DocModel/DocVariable'; import type { DocVariable } from '~/DocModel/DocVariable';
export interface VariableProps { export function Variable({ data }: { data: ReturnType<DocVariable['toJSON']> }) {
data: ReturnType<DocVariable['toJSON']>;
}
export function Variable({ data }: VariableProps) {
return <DocContainer name={data.name} kind={data.kind} excerpt={data.excerpt} summary={data.summary} />; return <DocContainer name={data.name} kind={data.kind} excerpt={data.excerpt} summary={data.summary} />;
} }

View File

@@ -5,11 +5,10 @@ export type DocItemJSON = ReturnType<DocItem['toJSON']>;
export const MemberContext = createContext<DocItemJSON | undefined>(undefined); export const MemberContext = createContext<DocItemJSON | undefined>(undefined);
export interface MemberProviderProps { export const MemberProvider = ({
member,
children,
}: {
member: DocItemJSON | undefined; member: DocItemJSON | undefined;
children: React.ReactNode; children: React.ReactNode;
} }) => <MemberContext.Provider value={member}>{children}</MemberContext.Provider>;
export const MemberProvider = ({ member, children }: MemberProviderProps) => (
<MemberContext.Provider value={member}>{children}</MemberContext.Provider>
);

View File

@@ -2,7 +2,7 @@ import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server'; import type { NextRequest } from 'next/server';
export default function middleware(request: NextRequest) { export default function middleware(request: NextRequest) {
return NextResponse.rewrite(new URL('/docs/main/packages/builders', request.url)); return NextResponse.redirect(new URL('/docs/main/packages/builders', request.url));
} }
export const config = { export const config = {

View File

@@ -1,8 +1,57 @@
import { ColorScheme, ColorSchemeProvider, MantineProvider } from '@mantine/core';
import { useColorScheme } from '@mantine/hooks';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import '@unocss/reset/normalize.css'; import Head from 'next/head';
import { useEffect, useState } from 'react';
import { RouterTransition } from '~/components/RouterTransition';
import '../styles/unocss.css'; import '../styles/unocss.css';
import '../styles/main.css'; import '../styles/main.css';
export default function MyApp({ Component, pageProps }: AppProps) { export default function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />; const preferredColorScheme = useColorScheme('dark', { getInitialValueInEffect: true });
const [colorScheme, setColorScheme] = useState<ColorScheme>(preferredColorScheme);
const toggleColorScheme = (value?: ColorScheme) =>
setColorScheme(value ?? (colorScheme === 'dark' ? 'light' : 'dark'));
useEffect(() => {
setColorScheme(preferredColorScheme);
}, [preferredColorScheme]);
return (
<>
<Head>
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
</Head>
<ColorSchemeProvider colorScheme={colorScheme} toggleColorScheme={toggleColorScheme}>
<MantineProvider
theme={{
fontFamily: 'Inter',
colorScheme,
colors: {
blurple: [
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
'#5865F2',
],
},
primaryColor: 'blurple',
}}
withCSSVariables
withNormalizeCSS
withGlobalStyles
>
<RouterTransition />
<Component {...pageProps} />
</MantineProvider>
</ColorSchemeProvider>
</>
);
} }

View File

@@ -1,24 +1,19 @@
import { Html, Head, Main, NextScript } from 'next/document'; import { createGetInitialProps } from '@mantine/next';
import Document, { Html, Head, Main, NextScript } from 'next/document';
export default function Document() { const getInitialProps = createGetInitialProps();
return ( export default class _Document extends Document {
<Html lang="en"> public static override getInitialProps = getInitialProps;
<Head />
<body> public override render() {
<script return (
dangerouslySetInnerHTML={{ <Html>
__html: `(() => { <Head />
const prefersDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches; <body>
const persistedColorPreference = localStorage.getItem('theme') || 'auto'; <Main />
if (persistedColorPreference === 'dark' || (prefersDarkMode && persistedColorPreference !== 'light')) { <NextScript />
document.documentElement.classList.toggle('dark', true); </body>
} </Html>
})();`, );
}} }
/>
<Main />
<NextScript />
</body>
</Html>
);
} }

View File

@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-throw-literal */ /* eslint-disable @typescript-eslint/no-throw-literal */
import { Box } from '@mantine/core';
import { ApiFunction } from '@microsoft/api-extractor-model'; import { ApiFunction } from '@microsoft/api-extractor-model';
import type { GetStaticPaths, GetStaticProps } from 'next/types'; import type { GetStaticPaths, GetStaticProps } from 'next/types';
import type { DocClass } from '~/DocModel/DocClass'; import type { DocClass } from '~/DocModel/DocClass';
@@ -7,8 +8,7 @@ import type { DocFunction } from '~/DocModel/DocFunction';
import type { DocInterface } from '~/DocModel/DocInterface'; import type { DocInterface } from '~/DocModel/DocInterface';
import type { DocTypeAlias } from '~/DocModel/DocTypeAlias'; import type { DocTypeAlias } from '~/DocModel/DocTypeAlias';
import type { DocVariable } from '~/DocModel/DocVariable'; import type { DocVariable } from '~/DocModel/DocVariable';
import type { ItemListProps } from '~/components/ItemSidebar'; import { SidebarLayout, type SidebarLayoutProps } from '~/components/SidebarLayout';
import { SidebarLayout } from '~/components/SidebarLayout';
import { Class } from '~/components/model/Class'; import { Class } from '~/components/model/Class';
import { Enum } from '~/components/model/Enum'; import { Enum } from '~/components/model/Enum';
import { Function } from '~/components/model/Function'; import { Function } from '~/components/model/Function';
@@ -26,10 +26,6 @@ export const getStaticPaths: GetStaticPaths = async () => {
const pkgs = ( const pkgs = (
await Promise.all( await Promise.all(
packages.map(async (packageName) => { packages.map(async (packageName) => {
if (packageName === 'rest') {
return { params: { slug: ['main', 'packages', packageName] } };
}
try { try {
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/main.api.json`); const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/main.api.json`);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
@@ -40,16 +36,21 @@ export const getStaticPaths: GetStaticPaths = async () => {
return [ return [
{ params: { slug: ['main', 'packages', packageName] } }, { params: { slug: ['main', 'packages', packageName] } },
...getMembers(pkg!).map((member) => { ...getMembers(pkg!)
if (member.kind === 'Function' && member.overloadIndex) { // Filtering out enum `RESTEvents` because of interface with similar name `RestEvents`
return { // causing next.js export to error
params: { .filter((member) => member.name !== 'RESTEvents')
slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`], .map((member) => {
}, if (member.kind === 'Function' && member.overloadIndex) {
}; return {
} params: {
return { params: { slug: ['main', 'packages', packageName, member.name] } }; slug: ['main', 'packages', packageName, `${member.name}:${member.overloadIndex}`],
}), },
};
}
return { params: { slug: ['main', 'packages', packageName, member.name] } };
}),
]; ];
} catch { } catch {
return { params: { slug: ['', '', '', ''] } }; return { params: { slug: ['', '', '', ''] } };
@@ -77,8 +78,8 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
const model = createApiModel(data); const model = createApiModel(data);
const pkg = findPackage(model, packageName); const pkg = findPackage(model, packageName);
let { containerKey, name } = findMember(model, packageName, memberName)!; let { containerKey, name } = findMember(model, packageName, memberName) ?? {};
if (overloadIndex) { if (name && overloadIndex) {
containerKey = ApiFunction.getContainerKey(name, parseInt(overloadIndex, 10)); containerKey = ApiFunction.getContainerKey(name, parseInt(overloadIndex, 10));
} }
@@ -87,7 +88,8 @@ export const getStaticProps: GetStaticProps = async ({ params }) => {
packageName, packageName,
data: { data: {
members: pkg ? getMembers(pkg) : [], members: pkg ? getMembers(pkg) : [],
member: memberName ? findMemberByKey(model, packageName, containerKey)?.toJSON() ?? null : null, member:
memberName && containerKey ? findMemberByKey(model, packageName, containerKey)?.toJSON() ?? null : null,
}, },
}, },
}; };
@@ -116,13 +118,11 @@ const member = (props: any) => {
case 'Enum': case 'Enum':
return <Enum data={props as ReturnType<DocEnum['toJSON']>} />; return <Enum data={props as ReturnType<DocEnum['toJSON']>} />;
default: default:
return <div>Cannot render that item type</div>; return <Box>Cannot render that item type</Box>;
} }
}; };
export default function Slug( export default function Slug(props: Partial<SidebarLayoutProps & { error?: string }>) {
props: Partial<ItemListProps & { error?: string; data: { member: ReturnType<typeof findMember> } }>,
) {
return props.error ? ( return props.error ? (
<div className="flex max-w-full h-full bg-white dark:bg-dark">{props.error}</div> <div className="flex max-w-full h-full bg-white dark:bg-dark">{props.error}</div>
) : ( ) : (

View File

@@ -1,3 +0,0 @@
export default function DocsLanding() {
return <div>Documentation</div>;
}

View File

@@ -1,73 +1,117 @@
import { createStyles, Container, Title, Button, Group, Text, Center } from '@mantine/core';
import Image from 'next/future/image'; import Image from 'next/future/image';
import Link from 'next/link'; import Link from 'next/link';
import { forwardRef, MouseEventHandler, Ref } from 'react';
import codeSample from '../assets/code-sample.png'; import codeSample from '../assets/code-sample.png';
import logo from '../assets/djs_logo_rainbow_400x400.png';
import vercelLogo from '../assets/powered-by-vercel.svg'; import vercelLogo from '../assets/powered-by-vercel.svg';
import text from '../text.json';
interface ButtonProps { const useStyles = createStyles((theme) => ({
label: string; inner: {
href?: string; display: 'flex',
ref?: Ref<HTMLAnchorElement>; justifyContent: 'space-between',
onClick?: MouseEventHandler<HTMLAnchorElement>; alignItems: 'center',
} paddingTop: theme.spacing.xl * 4,
paddingBottom: theme.spacing.xl * 4,
// eslint-disable-next-line react/display-name [theme.fn.smallerThan('md')]: {
const LinkButton = forwardRef<HTMLAnchorElement, ButtonProps>(({ label, onClick, href }: ButtonProps, ref) => ( flexDirection: 'column',
<a gap: 50,
href={href} },
onClick={onClick} },
ref={ref}
className="no-underline max-h-[70px] bg-blurple px-3 py-4 rounded-lg font-semibold text-white" content: {
> maxWidth: 480,
{label} marginRight: theme.spacing.xl * 3,
</a>
)); [theme.fn.smallerThan('md')]: {
marginRight: 0,
},
},
title: {
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
fontSize: 44,
lineHeight: 1.2,
fontWeight: 900,
[theme.fn.smallerThan('xs')]: {
fontSize: 28,
},
},
highlight: {
position: 'relative',
backgroundColor: theme.fn.variant({ variant: 'light', color: theme.primaryColor }).background,
borderRadius: theme.radius.sm,
padding: '4px 12px',
},
control: {
[theme.fn.smallerThan('xs')]: {
flex: 1,
},
},
image: {
flex: 1,
height: '100%',
maxWidth: 550,
[theme.fn.smallerThan('xs')]: {
maxWidth: 350,
},
},
vercel: {
height: '100%',
maxWidth: 250,
paddingBottom: theme.spacing.xl * 4,
},
}));
export default function IndexRoute() { export default function IndexRoute() {
const { classes } = useStyles();
return ( return (
<main className="w-full max-w-full max-h-full h-full flex-col bg-white dark:bg-dark overflow-y-auto"> <div>
<div className="flex h-[65px] sticky top-0 border-b border-gray justify-center px-10 bg-white dark:bg-dark"> <Container size="lg">
<div className="flex items-center w-full max-w-[1100px] justify-between"> <div className={classes.inner}>
<div className="h-[50px] w-[50px] rounded-lg overflow-hidden"> <div className={classes.content}>
<Image className="h-[50px] w-[50px]" src={logo} /> <Title className={classes.title}>
</div> The <span className={classes.highlight}>most popular</span> way to build Discord <br /> bots.
<div className="flex flex-row space-x-8"> </Title>
<Link href="/docs" passHref> <Text color="dimmed" mt="md">
<a className="no-underline text-blurple font-semibold">Docs</a> discord.js is a powerful Node.js module that allows you to interact with the Discord API very easily. It
</Link> takes a much more object-oriented approach than most other JS Discord libraries, making your bot&apos;s
<a className="text-blurple font-semibold">Guide</a> code significantly tidier and easier to comprehend.
</div> </Text>
</div>
</div> <Group mt={30}>
<div className="xl:flex xl:flex-col xl:justify-center w-full max-w-full box-border p-10">
<div className="flex flex-col xl:flex-row grow max-w-[1100px] pb-10 space-y-10 xl:space-x-20 place-items-center place-self-center">
<div className="flex flex-col max-w-[800px] lt-xl:items-center">
<h1 className="font-bold text-6xl text-blurple my-2">{text.heroTitle}</h1>
<p className="text-xl text-dark-100 dark:text-gray-300">{text.heroDescription}</p>
<div className="flex flew-row space-x-4">
<LinkButton label="Read the guide" />
<Link href="/docs" passHref> <Link href="/docs" passHref>
<LinkButton label="Check out the docs" /> <Button component="a" radius="xl" size="md" className={classes.control}>
Docs
</Button>
</Link> </Link>
</div> <Link href="https://discordjs.guide" passHref>
</div> <Button component="a" variant="default" radius="xl" size="md" className={classes.control}>
<div className="sm:flex sm:grow sm:shrink h-full xl:items-center hidden"> Guide
<Image src={codeSample} className="max-w-[600px] h-full rounded-xl shadow-md overflow-hidden" /> </Button>
</Link>
</Group>
</div> </div>
<Image src={codeSample} className={classes.image} />
</div> </div>
<div className="flex place-content-center"> <Center>
<a href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" title="Vercel"> <Link href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss" passHref>
<Image <a title="Vercel">
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment <Image
src={vercelLogo} // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
alt="Vercel" src={vercelLogo}
className="max-w-[250px] shadow-md overflow-hidden" alt="Vercel"
/> className={classes.vercel}
</a> />
</div> </a>
</div> </Link>
</main> </Center>
</Container>
</div>
); );
} }

View File

@@ -1,4 +0,0 @@
{
"heroTitle": "The most popular way to build Discord bots.",
"heroDescription": "discord.js is a powerful Node.js module that allows you to interact with the Discord API very easily. It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend."
}

View File

@@ -5,8 +5,8 @@
"scripts": { "scripts": {
"test": "vitest run", "test": "vitest run",
"build": "unbuild", "build": "unbuild",
"lint": "prettier --check . && eslint src __tests__ --ext mjs,js,ts", "lint": "prettier --check . && TIMING=1 eslint src __tests__ --ext mjs,js,ts",
"format": "prettier --write . && eslint src __tests__ --ext mjs,js,ts --fix", "format": "prettier --write . && TIMING=1 eslint src __tests__ --ext mjs,js,ts --fix",
"docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local", "docs": "docgen -i src/index.ts -c docs/index.json -o docs/docs.json --typescript && api-extractor run --local",
"prepack": "yarn build && yarn lint", "prepack": "yarn build && yarn lint",
"changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/ws/*'", "changelog": "git cliff --prepend ./CHANGELOG.md -u -c ./cliff.toml -r ../../ --include-path 'packages/ws/*'",

View File

@@ -3,11 +3,15 @@
"pipeline": { "pipeline": {
"build": { "build": {
"dependsOn": ["^build"], "dependsOn": ["^build"],
"outputs": ["**/dist/**"] "outputs": ["dist/**"]
},
"@discordjs/website#build": {
"dependsOn": ["^build"],
"outputs": [".next/**"]
}, },
"test": { "test": {
"dependsOn": ["^build"], "dependsOn": ["^build"],
"outputs": ["**/coverage/**"] "outputs": []
}, },
"lint": { "lint": {
"dependsOn": ["^build"], "dependsOn": ["^build"],
@@ -19,7 +23,7 @@
}, },
"docs": { "docs": {
"dependsOn": ["^build"], "dependsOn": ["^build"],
"outputs": ["**/docs/docs.json", "**/docs/docs.api.json"] "outputs": ["docs/docs.json", "docs/docs.api.json"]
}, },
"changelog": { "changelog": {
"dependsOn": ["^build"], "dependsOn": ["^build"],

1403
yarn.lock

File diff suppressed because it is too large Load Diff