mirror of
https://github.com/discordjs/discord.js.git
synced 2026-05-23 12:00:09 +00:00
Compare commits
141 Commits
@discordjs
...
@discordjs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d63bcbb9d4 | ||
|
|
82f4827ee5 | ||
|
|
e470e088ab | ||
|
|
3d6fa248c0 | ||
|
|
273ba45e27 | ||
|
|
7a5134459c | ||
|
|
6540914b4a | ||
|
|
627511d652 | ||
|
|
a7b55c1460 | ||
|
|
1b151db59c | ||
|
|
aa8c57dab6 | ||
|
|
b14604abde | ||
|
|
73300c75fa | ||
|
|
3dede75621 | ||
|
|
3f555d5ddf | ||
|
|
976b234e9d | ||
|
|
c48ff5e420 | ||
|
|
6acf759f63 | ||
|
|
fb70df817c | ||
|
|
86959bad49 | ||
|
|
a0d6bc49c6 | ||
|
|
45faa19982 | ||
|
|
8f552a0e17 | ||
|
|
3fca638a84 | ||
|
|
4e4cbb3418 | ||
|
|
52f56d3c2e | ||
|
|
40b504a208 | ||
|
|
322cb99049 | ||
|
|
11d195d04f | ||
|
|
f13ff5c6d3 | ||
|
|
5e4331062b | ||
|
|
fff3602a8a | ||
|
|
72c30d1578 | ||
|
|
2cb1000a3a | ||
|
|
8a39c14921 | ||
|
|
4ae029dfe2 | ||
|
|
2849af0118 | ||
|
|
90d192078f | ||
|
|
b0f9b9c344 | ||
|
|
6bcebd4867 | ||
|
|
1efa95337f | ||
|
|
2f4bdf7394 | ||
|
|
4b9b62416b | ||
|
|
208378b214 | ||
|
|
6e65b45b31 | ||
|
|
2b260bd4bb | ||
|
|
cab692409d | ||
|
|
0fe503b516 | ||
|
|
999bc2069e | ||
|
|
b89bd6f67c | ||
|
|
5235ad5bfe | ||
|
|
3bd8458e09 | ||
|
|
44696c0a6a | ||
|
|
b5d8b69452 | ||
|
|
7430c8e4c8 | ||
|
|
1a10f48bc3 | ||
|
|
e592852e42 | ||
|
|
8376e2dbcd | ||
|
|
ed68a1af5b | ||
|
|
ebf9991df7 | ||
|
|
31e67c4316 | ||
|
|
2f4bfedca1 | ||
|
|
2127b32d26 | ||
|
|
12553da135 | ||
|
|
decbce4010 | ||
|
|
9922151266 | ||
|
|
f0120538bb | ||
|
|
650f4ddfb2 | ||
|
|
56d086022f | ||
|
|
909a02e2ad | ||
|
|
ac0851b5b1 | ||
|
|
1ef42e4e7b | ||
|
|
db346e014f | ||
|
|
8f5b167483 | ||
|
|
ec37f137fd | ||
|
|
f9828034cd | ||
|
|
cb77fd02d0 | ||
|
|
7e06f68185 | ||
|
|
d2754802cc | ||
|
|
7ae0c529f2 | ||
|
|
7332b7bae3 | ||
|
|
33a82b7bce | ||
|
|
d0c82561b8 | ||
|
|
256677b435 | ||
|
|
ee7d2fd4c8 | ||
|
|
65bc0adbf4 | ||
|
|
fd4ba5eaba | ||
|
|
153d2403ad | ||
|
|
8ed5c1beb6 | ||
|
|
09f65b724b | ||
|
|
291f36cd73 | ||
|
|
b2fabd130a | ||
|
|
abefc4f809 | ||
|
|
d6873b7159 | ||
|
|
017f9b1ed4 | ||
|
|
e74aa7f6b0 | ||
|
|
ff85481d3e | ||
|
|
68c9cb37bc | ||
|
|
5152abf728 | ||
|
|
8b400ca975 | ||
|
|
88cd9d9060 | ||
|
|
5a70057826 | ||
|
|
0bcc18a0bd | ||
|
|
b5213664fa | ||
|
|
6e348ffd1d | ||
|
|
e7cbc1bf11 | ||
|
|
53d8e87d7f | ||
|
|
fc10774461 | ||
|
|
68d5712dea | ||
|
|
b2658ce504 | ||
|
|
3cc50b9e95 | ||
|
|
c0e3629f64 | ||
|
|
7083df7cef | ||
|
|
caeb1cbfdb | ||
|
|
4f3c13628e | ||
|
|
94097d365a | ||
|
|
8af0b38b6c | ||
|
|
66097e0cb0 | ||
|
|
bf9aa1858d | ||
|
|
179392d6d7 | ||
|
|
ea807e3eed | ||
|
|
ef29b5e51f | ||
|
|
cb3826ce6d | ||
|
|
d7a45a0567 | ||
|
|
49b91315f7 | ||
|
|
5ffabb119f | ||
|
|
eb46e3a5ad | ||
|
|
e8f63617a2 | ||
|
|
b05be9ec08 | ||
|
|
9d8179c6a7 | ||
|
|
f92be4fb94 | ||
|
|
c39faa94f6 | ||
|
|
7b7cc6fde4 | ||
|
|
05fbe992e3 | ||
|
|
698983b203 | ||
|
|
8b7ce2b61d | ||
|
|
e17323c15c | ||
|
|
f75f13d75b | ||
|
|
d587467a38 | ||
|
|
5bfb61d5b8 | ||
|
|
c5d5840fef |
@@ -20,19 +20,37 @@ dist/
|
||||
# Miscellaneous
|
||||
.tmp/
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
!.vscode/settings.json
|
||||
.idea/
|
||||
.DS_Store
|
||||
.turbo
|
||||
tsconfig.tsbuildinfo
|
||||
coverage/
|
||||
__tests__/
|
||||
|
||||
# yarn
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/cache
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Cache
|
||||
.prettiercache
|
||||
.eslintcache
|
||||
|
||||
# Docker specific
|
||||
.cliff-jumperrc.json
|
||||
api-extractor.json
|
||||
.eslintrc.json
|
||||
.lintstagedrc.cjs
|
||||
.lintstagedrc.cjs
|
||||
.prettierignore
|
||||
.prettierrc.js
|
||||
.prettierrc.cjs
|
||||
cliff.toml
|
||||
CHANGELOG.md
|
||||
README.md
|
||||
tsconfig.eslint.json
|
||||
docs/
|
||||
|
||||
19
.github/CONTRIBUTING.md
vendored
19
.github/CONTRIBUTING.md
vendored
@@ -17,6 +17,25 @@ To get ready to work on the codebase, please do the following:
|
||||
5. Run `yarn test` to run ESLint and ensure any JSDoc changes are valid
|
||||
6. [Submit a pull request](https://github.com/discordjs/discord.js/compare) (Make sure you follow the [conventional commit format](https://github.com/discordjs/discord.js/blob/main/.github/COMMIT_CONVENTION.md))
|
||||
|
||||
## Testing changes locally
|
||||
|
||||
If you want to test changes you've made locally, you can do so by using `yarn link`. This will create a symlink to your local copy of the discord.js libraries.
|
||||
|
||||
1. Create a new directory `mkdir discordjs-test` and move into it `cd discordjs-test`
|
||||
2. Initialize a new yarn 3 project `yarn init -2`
|
||||
3. Disable pnp `yarn config set nodeLinker node-modules`
|
||||
4. Now link the local discord.js project you cloned earlier `yarn link -A {PATH_TO_DISCORDJS_REPO}`
|
||||
5. Install packages you'd like to test locally `yarn add discord.js@latest`, `yarn add @discordjs/rest@latest`, etc. **Note: Make sure you use `latest` tag or else yarn will try to install the remote package from npm**
|
||||
6. Import the package in your source code and test them out!
|
||||
|
||||
### Working with TypeScript packages
|
||||
|
||||
When testing local changes, you may notice you need to manually recompile TypeScript projects on every change in order to get the latest code changes to test locally.
|
||||
|
||||
To avoid this you can use the `--watch` parameter in the package build script to automatically recompile the project when changes are detected.
|
||||
|
||||
For example, to automatically recompile the `@discordjs/rest` project when changes are detected, run `yarn turbo run build --filter=@discordjs/rest -- --watch` in the root folder of where you cloned the discord.js repo.
|
||||
|
||||
## Adding new packages
|
||||
|
||||
If you'd like to create another package under the `@discordjs` organization run the following command:
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
7
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -12,11 +12,14 @@ body:
|
||||
label: Which package is this bug report for?
|
||||
options:
|
||||
- discord.js
|
||||
- brokers
|
||||
- builders
|
||||
- collection
|
||||
- rest
|
||||
- core
|
||||
- formatters
|
||||
- proxy
|
||||
- proxy-container
|
||||
- rest
|
||||
- voice
|
||||
- ws
|
||||
validations:
|
||||
@@ -131,6 +134,8 @@ body:
|
||||
- DirectMessageTyping
|
||||
- MessageContent
|
||||
- GuildScheduledEvents
|
||||
- AutoModerationConfiguration
|
||||
- AutoModerationExecution
|
||||
multiple: true
|
||||
validations:
|
||||
required: true
|
||||
|
||||
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
5
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -14,11 +14,14 @@ body:
|
||||
label: Which package is the feature request for?
|
||||
options:
|
||||
- discord.js
|
||||
- brokers
|
||||
- builders
|
||||
- collection
|
||||
- rest
|
||||
- core
|
||||
- formatters
|
||||
- proxy
|
||||
- proxy-container
|
||||
- rest
|
||||
- voice
|
||||
- ws
|
||||
validations:
|
||||
|
||||
16
.github/labeler.yml
vendored
16
.github/labeler.yml
vendored
@@ -4,19 +4,27 @@ apps:guide:
|
||||
apps:website:
|
||||
- apps/website/*
|
||||
- apps/website/**/*
|
||||
|
||||
packages:brokers:
|
||||
- packages/brokers/*
|
||||
- packages/brokers/**/*
|
||||
packages:builders:
|
||||
- packages/builders/*
|
||||
- packages/builders/**/*
|
||||
packages:collection:
|
||||
- packages/collection/*
|
||||
- packages/collection/**/*
|
||||
packages:core:
|
||||
- packages/core/*
|
||||
- packages/core/**/*
|
||||
packages:discord.js:
|
||||
- packages/discord.js/*
|
||||
- packages/discord.js/**/*
|
||||
packages:docgen:
|
||||
- packages/docgen/*
|
||||
- packages/docgen/**/*
|
||||
packages:formatters:
|
||||
- packages/formatters/*
|
||||
- packages/formatters/**/*
|
||||
packages:proxy:
|
||||
- packages/proxy/*
|
||||
- packages/proxy/**/*
|
||||
@@ -26,9 +34,9 @@ packages:proxy-container:
|
||||
packages:rest:
|
||||
- packages/rest/*
|
||||
- packages/rest/**/*
|
||||
packages/ui:
|
||||
- packages:ui/*
|
||||
- packages:ui/**/*
|
||||
packages:ui:
|
||||
- packages/ui/*
|
||||
- packages/ui/**/*
|
||||
packages:util:
|
||||
- packages/util/*
|
||||
- packages/util/**/*
|
||||
|
||||
14
.github/labels.yml
vendored
14
.github/labels.yml
vendored
@@ -20,10 +20,10 @@
|
||||
color: 0075ca
|
||||
- name: dependencies
|
||||
color: 276bd1
|
||||
- name: discussion
|
||||
color: b6b1f9
|
||||
- name: discord
|
||||
color: '5663e9'
|
||||
- name: discussion
|
||||
color: b6b1f9
|
||||
- name: documentation
|
||||
color: 0075ca
|
||||
- name: duplicate
|
||||
@@ -40,24 +40,30 @@
|
||||
color: 4b1f8e
|
||||
- name: help wanted
|
||||
color: '008672'
|
||||
- name: interactions
|
||||
color: 80c042
|
||||
- name: in progress
|
||||
color: ffccd7
|
||||
- name: in review
|
||||
color: aed5fc
|
||||
- name: interactions
|
||||
color: 80c042
|
||||
- name: invalid
|
||||
color: e4e669
|
||||
- name: need repro
|
||||
color: c66037
|
||||
- name: packages:brokers
|
||||
color: fbca04
|
||||
- name: packages:builders
|
||||
color: fbca04
|
||||
- name: packages:collection
|
||||
color: fbca04
|
||||
- name: packages:core
|
||||
color: fbca04
|
||||
- name: packages:discord.js
|
||||
color: fbca04
|
||||
- name: packages:docgen
|
||||
color: fbca04
|
||||
- name: packages:formatters
|
||||
color: fbca04
|
||||
- name: packages:proxy
|
||||
color: fbca04
|
||||
- name: packages:proxy-container
|
||||
|
||||
33
.github/workflows/documentation.yml
vendored
33
.github/workflows/documentation.yml
vendored
@@ -32,16 +32,15 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.inputs.ref || '' }}
|
||||
ref: ${{ inputs.ref || '' }}
|
||||
|
||||
- name: Install node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --immutable
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
@@ -68,13 +67,24 @@ jobs:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package: ['builders', 'collection', 'discord.js', 'proxy', 'rest', 'util', 'voice', 'ws']
|
||||
package:
|
||||
[
|
||||
'brokers',
|
||||
'builders',
|
||||
'collection',
|
||||
'core',
|
||||
'discord.js',
|
||||
'formatters',
|
||||
'proxy',
|
||||
'rest',
|
||||
'util',
|
||||
'voice',
|
||||
'ws',
|
||||
]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
BRANCH_NAME: ${{ github.event.inputs.ref || needs.build.outputs.BRANCH_NAME }}
|
||||
BRANCH_OR_TAG: ${{ github.event.inputs.ref_type || needs.build.outputs.BRANCH_OR_TAG }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
@@ -83,10 +93,9 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --immutable
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build actions
|
||||
run: yarn workspace @discordjs/actions build
|
||||
@@ -111,14 +120,14 @@ jobs:
|
||||
path: 'out'
|
||||
|
||||
- name: Extract package and semver from tag
|
||||
if: ${{ env.GITHUB_REF_TYPE == 'tag' }}
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
id: extract-tag
|
||||
uses: ./packages/actions/src/formatTag
|
||||
with:
|
||||
tag: ${{ env.GITHUB_REF_NAME }}
|
||||
tag: ${{ github.ref_name }}
|
||||
|
||||
- name: Move docs to correct directory
|
||||
if: ${{ env.GITHUB_REF_TYPE == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
|
||||
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.extract-tag.outputs.package }}
|
||||
SEMVER: ${{ steps.extract-tag.outputs.semver }}
|
||||
@@ -132,7 +141,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Move docs to correct directory
|
||||
if: ${{ env.GITHUB_REF_TYPE == 'branch' }}
|
||||
if: ${{ github.ref_type == 'branch' }}
|
||||
env:
|
||||
PACKAGE: ${{ matrix.package }}
|
||||
run: |
|
||||
|
||||
5
.github/workflows/npm-auto-deprecate.yml
vendored
5
.github/workflows/npm-auto-deprecate.yml
vendored
@@ -16,12 +16,11 @@ jobs:
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --immutable
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Deprecate versions
|
||||
run: 'yarn npm-deprecate --name "*dev*" --package @discordjs/builders @discordjs/collection discord.js @discordjs/proxy @discordjs/rest @discordjs/util @discordjs/voice @discordjs/ws'
|
||||
run: 'yarn npm-deprecate --name "*dev*" --package @discordjs/brokers @discordjs/builders @discordjs/collection @discordjs/core @discordjs/formatters discord.js @discordjs/proxy @discordjs/rest @discordjs/util @discordjs/voice @discordjs/ws'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
|
||||
9
.github/workflows/publish-dev.yml
vendored
9
.github/workflows/publish-dev.yml
vendored
@@ -10,10 +10,16 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- package: '@discordjs/brokers'
|
||||
folder: 'brokers'
|
||||
- package: '@discordjs/builders'
|
||||
folder: 'builders'
|
||||
- package: '@discordjs/collection'
|
||||
folder: 'collection'
|
||||
- package: '@discordjs/core'
|
||||
folder: 'core'
|
||||
- package: '@discordjs/formatters'
|
||||
folder: 'formatters'
|
||||
- package: 'discord.js'
|
||||
folder: 'discord.js'
|
||||
- package: '@discordjs/proxy'
|
||||
@@ -40,10 +46,9 @@ jobs:
|
||||
with:
|
||||
node-version: 16
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn --immutable
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
|
||||
41
.github/workflows/tests.yml
vendored
41
.github/workflows/tests.yml
vendored
@@ -15,27 +15,48 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --immutable
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
- name: Build dependencies (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn build --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]"
|
||||
|
||||
- name: ESLint
|
||||
run: yarn lint
|
||||
- name: Build dependencies (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn build --filter="...[HEAD^1]"
|
||||
|
||||
- name: Tests
|
||||
run: yarn test
|
||||
- name: ESLint (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn lint --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]" -- --format=compact
|
||||
|
||||
- name: Docs
|
||||
run: yarn docs
|
||||
- name: ESLint (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn lint --filter="...[HEAD^1]" -- --format=compact
|
||||
|
||||
- name: Tests (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn test --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]"
|
||||
|
||||
- name: Tests (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn test --filter="...[HEAD^1]"
|
||||
|
||||
- name: Docs (PR)
|
||||
if: ${{ github.event_name != 'push' }}
|
||||
run: yarn docs --filter="...[origin/${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || 'main' }}]"
|
||||
|
||||
- name: Docs (Push)
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: yarn docs --filter="...[HEAD^1]"
|
||||
|
||||
- name: Upload Coverage
|
||||
if: github.repository_owner == 'discordjs'
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -27,6 +27,7 @@ dist/
|
||||
.turbo
|
||||
tsconfig.tsbuildinfo
|
||||
coverage/
|
||||
out/
|
||||
|
||||
# yarn
|
||||
.pnp.*
|
||||
@@ -39,4 +40,4 @@ coverage/
|
||||
|
||||
# Cache
|
||||
.prettiercache
|
||||
.eslintcache
|
||||
.eslintcache
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
yarn build && yarn lint-staged
|
||||
yarn build:affected && yarn lint-staged
|
||||
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -9,6 +9,7 @@
|
||||
"christian-kohler.npm-intellisense",
|
||||
"christian-kohler.path-intellisense",
|
||||
"antfu.unocss",
|
||||
"astro-build.astro-vscode"
|
||||
"astro-build.astro-vscode",
|
||||
"unifiedjs.vscode-mdx"
|
||||
]
|
||||
}
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -8,9 +8,12 @@
|
||||
"source.fixAll": true,
|
||||
"source.organizeImports": false
|
||||
},
|
||||
"editor.trimAutoWhitespace": false,
|
||||
"files.associations": {
|
||||
"*.mdx": "markdown"
|
||||
},
|
||||
"unocss.root": "./packages/ui",
|
||||
"files.insertFinalNewline": true,
|
||||
"files.eol": "\n",
|
||||
"npm.packageManager": "yarn",
|
||||
"typescript.tsdk": "node_modules/typescript/lib"
|
||||
}
|
||||
|
||||
382
api-extractor.json
Normal file
382
api-extractor.json
Normal file
@@ -0,0 +1,382 @@
|
||||
/**
|
||||
* Config file for API Extractor. For more info, please visit: https://api-extractor.com
|
||||
*/
|
||||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
|
||||
|
||||
/**
|
||||
* Optionally specifies another JSON config file that this file extends from. This provides a way for
|
||||
* standard settings to be shared across multiple projects.
|
||||
*
|
||||
* If the path starts with "./" or "../", the path is resolved relative to the folder of the file that contains
|
||||
* the "extends" field. Otherwise, the first path segment is interpreted as an NPM package name, and will be
|
||||
* resolved using NodeJS require().
|
||||
*
|
||||
* SUPPORTED TOKENS: none
|
||||
* DEFAULT VALUE: ""
|
||||
*/
|
||||
// "extends": "./shared/api-extractor-base.json"
|
||||
// "extends": "my-package/include/api-extractor-base.json"
|
||||
|
||||
/**
|
||||
* Determines the "<projectFolder>" token that can be used with other config file settings. The project folder
|
||||
* typically contains the tsconfig.json and package.json config files, but the path is user-defined.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting.
|
||||
*
|
||||
* The default value for "projectFolder" is the token "<lookup>", which means the folder is determined by traversing
|
||||
* parent folders, starting from the folder containing api-extractor.json, and stopping at the first folder
|
||||
* that contains a tsconfig.json file. If a tsconfig.json file cannot be found in this way, then an error
|
||||
* will be reported.
|
||||
*
|
||||
* SUPPORTED TOKENS: <lookup>
|
||||
* DEFAULT VALUE: "<lookup>"
|
||||
*/
|
||||
// "projectFolder": "..",
|
||||
|
||||
/**
|
||||
* (REQUIRED) Specifies the .d.ts file to be used as the starting point for analysis. API Extractor
|
||||
* analyzes the symbols exported by this module.
|
||||
*
|
||||
* The file extension must be ".d.ts" and not ".ts".
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
*/
|
||||
"mainEntryPointFilePath": "<projectFolder>/dist/index.d.ts",
|
||||
|
||||
/**
|
||||
* A list of NPM package names whose exports should be treated as part of this package.
|
||||
*
|
||||
* For example, suppose that Webpack is used to generate a distributed bundle for the project "library1",
|
||||
* and another NPM package "library2" is embedded in this bundle. Some types from library2 may become part
|
||||
* of the exported API for library1, but by default API Extractor would generate a .d.ts rollup that explicitly
|
||||
* imports library2. To avoid this, we can specify:
|
||||
*
|
||||
* "bundledPackages": [ "library2" ],
|
||||
*
|
||||
* This would direct API Extractor to embed those types directly in the .d.ts rollup, as if they had been
|
||||
* local files for library1.
|
||||
*/
|
||||
"bundledPackages": [],
|
||||
|
||||
/**
|
||||
* Determines how the TypeScript compiler engine will be invoked by API Extractor.
|
||||
*/
|
||||
"compiler": {
|
||||
/**
|
||||
* Specifies the path to the tsconfig.json file to be used by API Extractor when analyzing the project.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* Note: This setting will be ignored if "overrideTsconfig" is used.
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<projectFolder>/tsconfig.json"
|
||||
*/
|
||||
// "tsconfigFilePath": "<projectFolder>/tsconfig.json",
|
||||
/**
|
||||
* Provides a compiler configuration that will be used instead of reading the tsconfig.json file from disk.
|
||||
* The object must conform to the TypeScript tsconfig schema:
|
||||
*
|
||||
* http://json.schemastore.org/tsconfig
|
||||
*
|
||||
* If omitted, then the tsconfig.json file will be read from the "projectFolder".
|
||||
*
|
||||
* DEFAULT VALUE: no overrideTsconfig section
|
||||
*/
|
||||
// "overrideTsconfig": {
|
||||
// . . .
|
||||
// }
|
||||
/**
|
||||
* This option causes the compiler to be invoked with the --skipLibCheck option. This option is not recommended
|
||||
* and may cause API Extractor to produce incomplete or incorrect declarations, but it may be required when
|
||||
* dependencies contain declarations that are incompatible with the TypeScript engine that API Extractor uses
|
||||
* for its analysis. Where possible, the underlying issue should be fixed rather than relying on skipLibCheck.
|
||||
*
|
||||
* DEFAULT VALUE: false
|
||||
*/
|
||||
// "skipLibCheck": true,
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the API report file (*.api.md) will be generated.
|
||||
*/
|
||||
"apiReport": {
|
||||
/**
|
||||
* (REQUIRED) Whether to generate an API report.
|
||||
*/
|
||||
"enabled": false
|
||||
|
||||
/**
|
||||
* The filename for the API report files. It will be combined with "reportFolder" or "reportTempFolder" to produce
|
||||
* a full file path.
|
||||
*
|
||||
* The file extension should be ".api.md", and the string should not contain a path separator such as "\" or "/".
|
||||
*
|
||||
* SUPPORTED TOKENS: <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<unscopedPackageName>.api.md"
|
||||
*/
|
||||
// "reportFileName": "<unscopedPackageName>.api.md",
|
||||
|
||||
/**
|
||||
* Specifies the folder where the API report file is written. The file name portion is determined by
|
||||
* the "reportFileName" setting.
|
||||
*
|
||||
* The API report file is normally tracked by Git. Changes to it can be used to trigger a branch policy,
|
||||
* e.g. for an API review.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<projectFolder>/temp/"
|
||||
*/
|
||||
// "reportFolder": "<projectFolder>/temp/",
|
||||
|
||||
/**
|
||||
* Specifies the folder where the temporary report file is written. The file name portion is determined by
|
||||
* the "reportFileName" setting.
|
||||
*
|
||||
* After the temporary file is written to disk, it is compared with the file in the "reportFolder".
|
||||
* If they are different, a production build will fail.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<projectFolder>/temp/"
|
||||
*/
|
||||
// "reportTempFolder": "<projectFolder>/temp/"
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the doc model file (*.api.json) will be generated.
|
||||
*/
|
||||
"docModel": {
|
||||
/**
|
||||
* (REQUIRED) Whether to generate a doc model file.
|
||||
*/
|
||||
"enabled": true,
|
||||
|
||||
/**
|
||||
* The output path for the doc model file. The file extension should be ".api.json".
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<projectFolder>/temp/<unscopedPackageName>.api.json"
|
||||
*/
|
||||
"apiJsonFilePath": "<projectFolder>/docs/docs.api.json"
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the .d.ts rollup file will be generated.
|
||||
*/
|
||||
"dtsRollup": {
|
||||
/**
|
||||
* (REQUIRED) Whether to generate the .d.ts rollup file.
|
||||
*/
|
||||
"enabled": false
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
|
||||
* This file will include all declarations that are exported by the main entry point.
|
||||
*
|
||||
* If the path is an empty string, then this file will not be written.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<projectFolder>/dist/<unscopedPackageName>.d.ts"
|
||||
*/
|
||||
// "untrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>.d.ts",
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release.
|
||||
* This file will include only declarations that are marked as "@public", "@beta", or "@alpha".
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: ""
|
||||
*/
|
||||
// "alphaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-alpha.d.ts",
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "beta" release.
|
||||
* This file will include only declarations that are marked as "@public" or "@beta".
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: ""
|
||||
*/
|
||||
// "betaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-beta.d.ts",
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated with trimming for a "public" release.
|
||||
* This file will include only declarations that are marked as "@public".
|
||||
*
|
||||
* If the path is an empty string, then this file will not be written.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: ""
|
||||
*/
|
||||
// "publicTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-public.d.ts",
|
||||
|
||||
/**
|
||||
* When a declaration is trimmed, by default it will be replaced by a code comment such as
|
||||
* "Excluded from this release type: exampleMember". Set "omitTrimmingComments" to true to remove the
|
||||
* declaration completely.
|
||||
*
|
||||
* DEFAULT VALUE: false
|
||||
*/
|
||||
// "omitTrimmingComments": true
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures how the tsdoc-metadata.json file will be generated.
|
||||
*/
|
||||
"tsdocMetadata": {
|
||||
/**
|
||||
* Whether to generate the tsdoc-metadata.json file.
|
||||
*
|
||||
* DEFAULT VALUE: true
|
||||
*/
|
||||
// "enabled": true,
|
||||
/**
|
||||
* Specifies where the TSDoc metadata file should be written.
|
||||
*
|
||||
* The path is resolved relative to the folder of the config file that contains the setting; to change this,
|
||||
* prepend a folder token such as "<projectFolder>".
|
||||
*
|
||||
* The default value is "<lookup>", which causes the path to be automatically inferred from the "tsdocMetadata",
|
||||
* "typings" or "main" fields of the project's package.json. If none of these fields are set, the lookup
|
||||
* falls back to "tsdoc-metadata.json" in the package folder.
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<lookup>"
|
||||
*/
|
||||
// "tsdocMetadataFilePath": "<projectFolder>/dist/tsdoc-metadata.json"
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies what type of newlines API Extractor should use when writing output files. By default, the output files
|
||||
* will be written with Windows-style newlines. To use POSIX-style newlines, specify "lf" instead.
|
||||
* To use the OS's default newline kind, specify "os".
|
||||
*
|
||||
* DEFAULT VALUE: "crlf"
|
||||
*/
|
||||
"newlineKind": "lf",
|
||||
|
||||
/**
|
||||
* Configures how API Extractor reports error and warning messages produced during analysis.
|
||||
*
|
||||
* There are three sources of messages: compiler messages, API Extractor messages, and TSDoc messages.
|
||||
*/
|
||||
"messages": {
|
||||
/**
|
||||
* Configures handling of diagnostic messages reported by the TypeScript compiler engine while analyzing
|
||||
* the input .d.ts files.
|
||||
*
|
||||
* TypeScript message identifiers start with "TS" followed by an integer. For example: "TS2551"
|
||||
*
|
||||
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
|
||||
*/
|
||||
"compilerMessageReporting": {
|
||||
/**
|
||||
* Configures the default routing for messages that don't match an explicit rule in this table.
|
||||
*/
|
||||
"default": {
|
||||
/**
|
||||
* Specifies whether the message should be written to the the tool's output log. Note that
|
||||
* the "addToApiReportFile" property may supersede this option.
|
||||
*
|
||||
* Possible values: "error", "warning", "none"
|
||||
*
|
||||
* Errors cause the build to fail and return a nonzero exit code. Warnings cause a production build fail
|
||||
* and return a nonzero exit code. For a non-production build (e.g. when "api-extractor run" includes
|
||||
* the "--local" option), the warning is displayed but the build will not fail.
|
||||
*
|
||||
* DEFAULT VALUE: "warning"
|
||||
*/
|
||||
"logLevel": "warning"
|
||||
|
||||
/**
|
||||
* When addToApiReportFile is true: If API Extractor is configured to write an API report file (.api.md),
|
||||
* then the message will be written inside that file; otherwise, the message is instead logged according to
|
||||
* the "logLevel" option.
|
||||
*
|
||||
* DEFAULT VALUE: false
|
||||
*/
|
||||
// "addToApiReportFile": false
|
||||
}
|
||||
|
||||
// "TS2551": {
|
||||
// "logLevel": "warning",
|
||||
// "addToApiReportFile": true
|
||||
// },
|
||||
//
|
||||
// . . .
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures handling of messages reported by API Extractor during its analysis.
|
||||
*
|
||||
* API Extractor message identifiers start with "ae-". For example: "ae-extra-release-tag"
|
||||
*
|
||||
* DEFAULT VALUE: See api-extractor-defaults.json for the complete table of extractorMessageReporting mappings
|
||||
*/
|
||||
"extractorMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
// "addToApiReportFile": false
|
||||
},
|
||||
|
||||
// Disable the following warning:
|
||||
// (ae-missing-release-tag) "x" is part of the package's API, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
"ae-missing-release-tag": {
|
||||
"logLevel": "none"
|
||||
}
|
||||
|
||||
// "ae-extra-release-tag": {
|
||||
// "logLevel": "warning",
|
||||
// "addToApiReportFile": true
|
||||
// },
|
||||
//
|
||||
// . . .
|
||||
},
|
||||
|
||||
/**
|
||||
* Configures handling of messages reported by the TSDoc parser when analyzing code comments.
|
||||
*
|
||||
* TSDoc message identifiers start with "tsdoc-". For example: "tsdoc-link-tag-unescaped-text"
|
||||
*
|
||||
* DEFAULT VALUE: A single "default" entry with logLevel=warning.
|
||||
*/
|
||||
"tsdocMessageReporting": {
|
||||
"default": {
|
||||
"logLevel": "warning"
|
||||
// "addToApiReportFile": false
|
||||
}
|
||||
|
||||
// "tsdoc-link-tag-unescaped-text": {
|
||||
// "logLevel": "warning",
|
||||
// "addToApiReportFile": true
|
||||
// },
|
||||
//
|
||||
// . . .
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,8 @@
|
||||
module.exports = require('../../.prettierrc.json');
|
||||
module.exports = {
|
||||
...require('../../.prettierrc.json'),
|
||||
plugins: [
|
||||
'prettier-plugin-astro',
|
||||
'prettier-plugin-tailwindcss', // MUST come last
|
||||
],
|
||||
pluginSearchDirs: false,
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { remarkCodeHike } from '@code-hike/mdx';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import compress from 'astro-compress';
|
||||
import critters from 'astro-critters';
|
||||
import { toString } from 'hast-util-to-string';
|
||||
import { type Node, toString } from 'hast-util-to-string';
|
||||
import { h } from 'hastscript';
|
||||
import { escape } from 'html-escaper';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
@@ -43,10 +43,42 @@ const createSROnlyLabel = (text: string) => {
|
||||
return node;
|
||||
};
|
||||
|
||||
const rootDir = new URL('../../', import.meta.url);
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
react(),
|
||||
mdx(),
|
||||
mdx({
|
||||
remarkPlugins: [[remarkCodeHike, { autoImport: false, theme: shikiThemeDarkPlus, lineNumbers: true }]],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
properties: {
|
||||
class:
|
||||
'relative inline-flex w-6 h-6 place-items-center place-content-center outline-0 text-black dark:text-white ml-2',
|
||||
},
|
||||
behavior: 'after',
|
||||
group: ({ tagName }: { tagName: string }) =>
|
||||
h('div', {
|
||||
class: `[&>*]:inline-block [&>h1]:m-0 [&>h2]:m-0 [&>h3]:m-0 [&>h4]:m-0 level-${tagName}`,
|
||||
tabIndex: -1,
|
||||
}),
|
||||
content: (heading: Node) => [
|
||||
h(
|
||||
`span.anchor-icon`,
|
||||
{
|
||||
ariaHidden: 'true',
|
||||
},
|
||||
LinkIcon,
|
||||
),
|
||||
createSROnlyLabel(toString(heading)),
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
}),
|
||||
image({
|
||||
serviceEntryPoint: '@astrojs/image/sharp',
|
||||
}),
|
||||
@@ -54,60 +86,31 @@ export default defineConfig({
|
||||
throttle: 3,
|
||||
}),
|
||||
Unocss({
|
||||
configFile: fileURLToPath(new URL('../../packages/ui/unocss.config.ts', import.meta.url)),
|
||||
configFile: fileURLToPath(new URL('unocss.config.ts', rootDir)),
|
||||
}),
|
||||
critters(),
|
||||
compress(),
|
||||
],
|
||||
markdown: {
|
||||
remarkPlugins: [[remarkCodeHike, { autoImport: false, theme: shikiThemeDarkPlus, lineNumbers: true }]],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
properties: {
|
||||
class:
|
||||
'relative inline-flex w-6 h-6 place-items-center place-content-center outline-0 text-black dark:text-white ml-2',
|
||||
},
|
||||
behavior: 'after',
|
||||
group: ({ tagName }) =>
|
||||
h('div', {
|
||||
class: `[&>*]:inline-block [&>h1]:m-0 [&>h2]:m-0 [&>h3]:m-0 [&>h4]:m-0 level-${tagName}`,
|
||||
tabIndex: -1,
|
||||
}),
|
||||
content: (heading) => [
|
||||
h(
|
||||
`span.anchor-icon`,
|
||||
{
|
||||
ariaHidden: 'true',
|
||||
},
|
||||
LinkIcon,
|
||||
),
|
||||
createSROnlyLabel(toString(heading)),
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
extendDefaultPlugins: true,
|
||||
syntaxHighlight: false,
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'ariakit/button': fileURLToPath(new URL('node_modules/ariakit/esm/button/index.js', import.meta.url)),
|
||||
'ariakit/disclosure': fileURLToPath(new URL('node_modules/ariakit/esm/disclosure/index.js', import.meta.url)),
|
||||
'ariakit/separator': fileURLToPath(new URL('node_modules/ariakit/esm/separator/index.js', import.meta.url)),
|
||||
'ariakit-utils/dom': fileURLToPath(new URL('node_modules/ariakit-utils/esm/dom.js', import.meta.url)),
|
||||
'ariakit-utils/events': fileURLToPath(new URL('node_modules/ariakit-utils/esm/events.js', import.meta.url)),
|
||||
'ariakit-utils/focus': fileURLToPath(new URL('node_modules/ariakit-utils/esm/focus.js', import.meta.url)),
|
||||
'ariakit-utils/hooks': fileURLToPath(new URL('node_modules/ariakit-utils/esm/hooks.js', import.meta.url)),
|
||||
'ariakit-utils/misc': fileURLToPath(new URL('node_modules/ariakit-utils/esm/misc.js', import.meta.url)),
|
||||
'ariakit-utils/platform': fileURLToPath(new URL('node_modules/ariakit-utils/esm/platform.js', import.meta.url)),
|
||||
'ariakit-utils/system': fileURLToPath(new URL('node_modules/ariakit-utils/esm/system.js', import.meta.url)),
|
||||
'react-icons/fi': fileURLToPath(new URL('node_modules/react-icons/fi/index.esm.js', import.meta.url)),
|
||||
'react-icons/vsc': fileURLToPath(new URL('node_modules/react-icons/vsc/index.esm.js', import.meta.url)),
|
||||
'react-use': fileURLToPath(new URL('node_modules/react-use/esm/index.js', import.meta.url)),
|
||||
'ariakit/button': fileURLToPath(new URL('node_modules/ariakit/esm/button/index.js', rootDir)),
|
||||
'ariakit/disclosure': fileURLToPath(new URL('node_modules/ariakit/esm/disclosure/index.js', rootDir)),
|
||||
'ariakit/separator': fileURLToPath(new URL('node_modules/ariakit/esm/separator/index.js', rootDir)),
|
||||
'ariakit-utils/dom': fileURLToPath(new URL('node_modules/ariakit-utils/esm/dom.js', rootDir)),
|
||||
'ariakit-utils/events': fileURLToPath(new URL('node_modules/ariakit-utils/esm/events.js', rootDir)),
|
||||
'ariakit-utils/focus': fileURLToPath(new URL('node_modules/ariakit-utils/esm/focus.js', rootDir)),
|
||||
'ariakit-utils/hooks': fileURLToPath(new URL('node_modules/ariakit-utils/esm/hooks.js', rootDir)),
|
||||
'ariakit-utils/misc': fileURLToPath(new URL('node_modules/ariakit-utils/esm/misc.js', rootDir)),
|
||||
'ariakit-utils/platform': fileURLToPath(new URL('node_modules/ariakit-utils/esm/platform.js', rootDir)),
|
||||
'ariakit-utils/system': fileURLToPath(new URL('node_modules/ariakit-utils/esm/system.js', rootDir)),
|
||||
'react-icons/fi': fileURLToPath(new URL('node_modules/react-icons/fi/index.esm.js', rootDir)),
|
||||
'react-icons/vsc': fileURLToPath(new URL('node_modules/react-icons/vsc/index.esm.js', rootDir)),
|
||||
'react-use': fileURLToPath(new URL('node_modules/react-use/esm/index.js', rootDir)),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -5,11 +5,13 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"build:prod": "yarn workspace @discordjs/ui run build && astro build",
|
||||
"dev": "astro dev",
|
||||
"build:local": "yarn build:prod",
|
||||
"build:prod": "yarn workspaces foreach -ptR run build && astro build",
|
||||
"dev": "yarn workspaces foreach -ptR run build && astro dev",
|
||||
"preview": "astro preview",
|
||||
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx,.astro",
|
||||
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx,.astro --fix"
|
||||
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx,.astro --format=pretty",
|
||||
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx,.astro --fix --format=pretty",
|
||||
"fmt": "yarn format"
|
||||
},
|
||||
"type": "module",
|
||||
"contributors": [
|
||||
@@ -34,53 +36,52 @@
|
||||
},
|
||||
"homepage": "https://discord.js.org",
|
||||
"dependencies": {
|
||||
"@astrojs/image": "^0.9.2",
|
||||
"@astrojs/mdx": "^0.11.4",
|
||||
"@astrojs/react": "^1.1.4",
|
||||
"@code-hike/mdx": "^0.7.4",
|
||||
"@discordjs/ui": "workspace:^",
|
||||
"ariakit": "^2.0.0-next.41",
|
||||
"cmdk": "^0.1.20",
|
||||
"meilisearch": "^0.28.0",
|
||||
"react": "^17.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-custom-scrollbars-2": "^4.5.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-use": "^17.4.0",
|
||||
"sharp": "^0.31.1",
|
||||
"shiki": "^0.11.1"
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.7.1",
|
||||
"react-use": "^17.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/prefetch": "^0.0.8",
|
||||
"@astrojs/image": "^0.12.0",
|
||||
"@astrojs/mdx": "^0.12.0",
|
||||
"@astrojs/prefetch": "^0.1.1",
|
||||
"@astrojs/react": "^1.2.2",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/node": "^16.11.64",
|
||||
"@types/react": "^17.0.50",
|
||||
"@types/react-dom": "^17.0.17",
|
||||
"@types/node": "16.18.4",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-syntax-highlighter": "^15.5.5",
|
||||
"@unocss/cli": "^0.45.29",
|
||||
"@unocss/reset": "^0.45.29",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"@vitest/coverage-c8": "^0.24.0",
|
||||
"astro": "^1.4.6",
|
||||
"astro-compress": "^1.0.11",
|
||||
"astro-critters": "^1.0.5",
|
||||
"@unocss/cli": "^0.47.5",
|
||||
"@unocss/reset": "^0.47.5",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"@vitest/coverage-c8": "^0.25.3",
|
||||
"astro": "^1.6.12",
|
||||
"astro-compress": "^1.1.15",
|
||||
"astro-critters": "^1.1.15",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-neon": "^0.1.37",
|
||||
"happy-dom": "^7.4.0",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-neon": "^0.1.40",
|
||||
"eslint-formatter-pretty": "^4.1.0",
|
||||
"happy-dom": "^7.7.0",
|
||||
"hast-util-to-string": "^2.0.0",
|
||||
"hastscript": "^7.0.2",
|
||||
"hastscript": "^7.1.0",
|
||||
"html-escaper": "^3.0.3",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-astro": "^0.5.5",
|
||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-astro": "^0.7.0",
|
||||
"prettier-plugin-tailwindcss": "^0.2.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-slug": "^5.0.1",
|
||||
"typescript": "^4.8.4",
|
||||
"unocss": "^0.45.29",
|
||||
"vercel": "^28.4.8",
|
||||
"vitest": "^0.24.0"
|
||||
"rehype-slug": "^5.1.0",
|
||||
"sharp": "^0.31.2",
|
||||
"shiki": "^0.11.1",
|
||||
"typescript": "^4.9.3",
|
||||
"unocss": "^0.47.5",
|
||||
"vercel": "^28.7.0",
|
||||
"vitest": "^0.25.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
13
apps/guide/src/components/PageButton.tsx
Normal file
13
apps/guide/src/components/PageButton.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
export function PageButton({ url, title, direction }: { direction: 'next' | 'prev'; title: string; url: string }) {
|
||||
return (
|
||||
<a
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple flex transform-gpu cursor-pointer select-none appearance-none flex-row flex-col place-items-center gap-2 rounded py-3 px-4 leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||
href={url}
|
||||
>
|
||||
<h3 className="text-md font-semibold">{title}</h3>
|
||||
<p className={`${direction === 'next' ? 'ml-auto' : 'mr-auto'} text-sm text-gray-600 dark:text-gray-400`}>
|
||||
{direction === 'next' ? 'Next Page' : 'Previous Page'}
|
||||
</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -4,13 +4,32 @@ import type { MarkdownLayoutProps } from 'astro';
|
||||
import { ExternalLink } from './ExternalLink.jsx';
|
||||
import { Navbar } from './Navbar.jsx';
|
||||
import { Outline } from './Outline.jsx';
|
||||
import { PageButton } from './PageButton.jsx';
|
||||
import { SidebarItems } from './SidebarItems.jsx';
|
||||
import { generateGithubURL } from '~/util/url.js';
|
||||
|
||||
const pages = await Astro.glob<{ category: string; title: string }>('../pages/**/*.mdx');
|
||||
|
||||
type Props = MarkdownLayoutProps<{}>;
|
||||
const { headings, url } = Astro.props;
|
||||
const { headings, url, frontmatter } = Astro.props;
|
||||
|
||||
const groupedPages = pages.reduce<Record<string, typeof pages>>((acc, page) => {
|
||||
const { category } = page.frontmatter;
|
||||
acc[category] ??= [];
|
||||
|
||||
acc[category]?.push(page);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// @ts-expect-error props is not typed
|
||||
const category = frontmatter.category as string;
|
||||
|
||||
const curCategoryPages = groupedPages[category];
|
||||
const curCategoryIndex = curCategoryPages!.findIndex((page) => page.url === url);
|
||||
|
||||
const pagePrev = curCategoryPages![curCategoryIndex - 1];
|
||||
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
||||
const pageNext = curCategoryPages![curCategoryIndex + 1];
|
||||
---
|
||||
|
||||
<script>
|
||||
@@ -56,15 +75,39 @@ const { headings, url } = Astro.props;
|
||||
>
|
||||
<Outline client:load headings={headings} />
|
||||
</div>
|
||||
<Separator className="my-5" />
|
||||
<div class="flex place-content-end">
|
||||
<ExternalLink client:load href={generateGithubURL(url!)} title="Edit this page on github" />
|
||||
<Separator className="my-5 border-light-800 dark:border-dark-100" />
|
||||
<div class="flex flex-col space-y-5">
|
||||
<div class="flex place-content-end">
|
||||
<ExternalLink client:load href={generateGithubURL(url!)} title="Edit this page on github" />
|
||||
</div>
|
||||
<div class="flex w-full">
|
||||
{
|
||||
pagePrev && (
|
||||
<PageButton
|
||||
direction="prev"
|
||||
title={pagePrev.frontmatter.title}
|
||||
url={pagePrev.url === '' ? '/' : pagePrev.url!}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<div class="ml-auto self-end justify-self-end">
|
||||
{
|
||||
pageNext && (
|
||||
<PageButton
|
||||
direction="next"
|
||||
title={pageNext.frontmatter.title}
|
||||
url={pageNext.url === '' ? '/' : pageNext.url!}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="h-76 md:h-52"></div>
|
||||
<footer
|
||||
class="dark:bg-dark-600 h-76 lg:pl-84 bg-light-600 fixed bottom-0 left-0 right-0 md:h-52 md:pl-4 md:pr-16 xl:pr-76"
|
||||
class="dark:bg-dark-600 h-76 lg:pl-84 bg-light-600 xl:pr-76 fixed bottom-0 left-0 right-0 md:h-52 md:pl-4 md:pr-16"
|
||||
>
|
||||
<div class="mx-auto flex max-w-6xl flex-col place-items-center gap-12 pt-12 lg:place-content-center">
|
||||
<div class="flex w-full flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
|
||||
|
||||
253
apps/guide/src/pages/additional-info/async-await.mdx
Normal file
253
apps/guide/src/pages/additional-info/async-await.mdx
Normal file
@@ -0,0 +1,253 @@
|
||||
---
|
||||
layout: '../../layouts/SidebarLayout.astro'
|
||||
title: Understanding async/await
|
||||
category: Additional info
|
||||
---
|
||||
|
||||
import { CH } from '@code-hike/mdx/components';
|
||||
import { Alert } from '@discordjs/ui';
|
||||
|
||||
# Understanding async/await
|
||||
|
||||
If you aren't very familiar with ECMAScript 2017, you may not know about async/await. It's a useful way to handle Promises in a hoisted manner. It's also slightly faster and increases overall readability.
|
||||
|
||||
## How do Promises work?
|
||||
|
||||
Before we can get into async/await, you should know what Promises are and how they work because async/await is just a way to handle Promises. If you know what Promises are and how to deal with them, you can skip this part.
|
||||
|
||||
Promises are a way to handle asynchronous tasks in JavaScript; they are the newer alternative to callbacks. A Promise has many similarities to a progress bar; they represent an unfinished and ongoing process. An excellent example of this is a request to a server (e.g., discord.js sends requests to Discord's API).
|
||||
|
||||
A Promise can have three states; pending, resolved, and rejected
|
||||
|
||||
The **pending** state means that the Promise still is ongoing and neither resolved nor rejected.
|
||||
The **resolved** state means that the Promise is done and executed without any errors.
|
||||
The **rejected** state means that the Promise encountered an error and could not execute correctly.
|
||||
|
||||
One important thing to know is that a Promise can only have one state simultaneously; it can never be pending and resolved, rejected and resolved, or pending and rejected. You may be asking, "How would that look in code?". Here is a small example:
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
This example uses ES6 code. If you do not know what that is, you should read up on that
|
||||
[here](/additional-info/es6-syntax.md).
|
||||
</Alert>
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
function deleteMessages(amount) {
|
||||
return new Promise((resolve) => {
|
||||
if (amount > 10) throw new Error("You can't delete more than 10 Messages at a time.");
|
||||
setTimeout(() => resolve('Deleted 10 messages.'), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
deleteMessages(5)
|
||||
.then((value) => {
|
||||
// `deleteMessages` is complete and has not encountered any errors
|
||||
// the resolved value will be the string "Deleted 10 messages"
|
||||
})
|
||||
.catch((error) => {
|
||||
// `deleteMessages` encountered an error
|
||||
// the error will be an Error Object
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
In this scenario, the _`deleteMessages`_ function returns a Promise. The _`.then()`_ method will trigger if the Promise resolves, and the _`.catch()`_ method if the Promise rejects. In the _`deleteMessages`_ function, the Promise is resolved after 2 seconds with the string "Deleted 10 messages.", so the _`.catch()`_ method will never be executed. You can also pass the _`.catch()`_ function as the second parameter of _`.then()`_.
|
||||
|
||||
## How to implement async/await
|
||||
|
||||
### Theory
|
||||
|
||||
The following information is essential to know before working with async/await. You can only use the _`await`_ keyword inside a function declared as _`async`_ (you put the _`async`_ keyword before the _`function`_ keyword or before the parameters when using a callback function).
|
||||
|
||||
A simple example would be:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
async function declaredAsAsync() {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
or
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
const declaredAsAsync = async () => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
You can use that as well if you use the arrow function as an event listener.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
client.on('event', async (first, last) => {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
An important thing to know is that a function declared as _`async`_ will always return a Promise. In addition to this, if you return something, the Promise will resolve with that value, and if you throw an error, it will reject the Promise with that error.
|
||||
|
||||
### Execution with discord.js code
|
||||
|
||||
Now that you know how Promises work and what they are used for, let's look at an example that handles multiple Promises. Let's say you want to react with letters (regional indicators) in a specific order. For this example, here's a basic template for a discord.js bot with some ES6 adjustments.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.once('ready', () => {
|
||||
console.log('I am ready!');
|
||||
});
|
||||
|
||||
client.on('interactionCreate', (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'react') {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
client.login('your-token-goes-here');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
If you don't know how Node.js asynchronous execution works, you would probably try something like this:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js mark=4:7
|
||||
client.on('interactionCreate', (interaction) => {
|
||||
// ...
|
||||
if (commandName === 'react') {
|
||||
const message = interaction.reply({ content: 'Reacting!', fetchReply: true });
|
||||
message.react('🇦');
|
||||
message.react('🇧');
|
||||
message.react('🇨');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
But since all of these methods are started at the same time, it would just be a race to which server request finished first, so there would be no guarantee that it would react at all (if the message isn't fetched) or in the order you wanted it to. In order to make sure it reacts after the message is sent and in order (a, b, c), you'd need to use the _`.then()`_ callback from the Promises that these methods return. The code would look like this:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js mark=4:12
|
||||
client.on('interactionCreate', (interaction) => {
|
||||
// ...
|
||||
if (commandName === 'react') {
|
||||
interaction.reply({ content: 'Reacting!', fetchReply: true }).then((message) => {
|
||||
message
|
||||
.react('🇦')
|
||||
.then(() => message.react('🇧'))
|
||||
.then(() => message.react('🇨'))
|
||||
.catch((error) => {
|
||||
// handle failure of any Promise rejection inside here
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
In this piece of code, the Promises are [chain resolved](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then#Chaining) with each other, and if one of the Promises gets rejected, the function passed to _`.catch()`_ gets called. Here's the same code but with async/await:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js mark=1,4:7
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
// ...
|
||||
if (commandName === 'react') {
|
||||
const message = await interaction.reply({ content: 'Reacting!', fetchReply: true });
|
||||
await message.react('🇦');
|
||||
await message.react('🇧');
|
||||
await message.react('🇨');
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
It's mostly the same code, but how would you catch Promise rejections now since _`.catch()`_ isn't there anymore? That is also a useful feature with async/await; the error will be thrown if you await it so that you can wrap the awaited Promises inside a try/catch, and you're good to go.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js mark=1,4:11
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
if (commandName === 'react') {
|
||||
try {
|
||||
const message = await interaction.reply({ content: 'Reacting!', fetchReply: true });
|
||||
await message.react('🇦');
|
||||
await message.react('🇧');
|
||||
await message.react('🇨');
|
||||
} catch (error) {
|
||||
// handle failure of any Promise rejection inside here
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
This code looks clean and is also easy to read.
|
||||
|
||||
So you may be asking, "How would I get the value the Promise resolved with?".
|
||||
|
||||
Let's look at an example where you want to delete a sent reply.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js mark=3:10
|
||||
client.on('interactionCreate', (interaction) => {
|
||||
// ...
|
||||
if (commandName === 'delete') {
|
||||
interaction
|
||||
.reply({ content: 'This message will be deleted.', fetchReply: true })
|
||||
.then((replyMessage) => setTimeout(() => replyMessage.delete(), 10000))
|
||||
.catch((error) => {
|
||||
// handle error
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The return value of a _`.reply()`_ with the _`fetchReply`_ option set to _`true`_ is a Promise which resolves with the reply when it has been sent, but how would the same code with async/await look?
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js mark=1,4:10
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
if (commandName === 'delete') {
|
||||
try {
|
||||
const replyMessage = await interaction.reply({ content: 'This message will be deleted.', fetchReply: true });
|
||||
setTimeout(() => replyMessage.delete(), 10000);
|
||||
} catch (error) {
|
||||
// handle error
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
With async/await, you can assign the awaited function to a variable representing the returned value. Now you know how you use async/await.
|
||||
127
apps/guide/src/pages/additional-info/collections.mdx
Normal file
127
apps/guide/src/pages/additional-info/collections.mdx
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
layout: '../../layouts/SidebarLayout.astro'
|
||||
title: Collections
|
||||
category: Additional info
|
||||
---
|
||||
|
||||
import { CH } from '@code-hike/mdx/components';
|
||||
import { Alert } from '@discordjs/ui';
|
||||
|
||||
# Collections
|
||||
|
||||
discord.js comes with a utility class known as _`Collection`_.
|
||||
It extends JavaScript's native _`Map`_ class, so it has all the _`Map`_ features and more!
|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
If you're not familiar with _`Map`_, read [MDN's page on
|
||||
it](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) before continuing. You
|
||||
should be familiar with _`Array`_
|
||||
[methods](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) as well. We will
|
||||
also use some ES6 features, so read up [here](/additional-info/es6-syntax.md) if you do not know what they are.
|
||||
</Alert>
|
||||
|
||||
A _`Map`_ allows for an association between unique keys and their values.
|
||||
For example, how can you transform every value or filter the entries in a _`Map`_ easily?
|
||||
This is the point of the _`Collection`_ class!
|
||||
|
||||
## Array-like Methods
|
||||
|
||||
Many of the methods on _`Collection`_ correspond to their namesake in _`Array`_. One of them is _`find`_:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
// Assume we have an array of users and a collection of the same users.
|
||||
array.find((u) => u.discriminator === '1000');
|
||||
collection.find((u) => u.discriminator === '1000');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The interface of the callback function is very similar between the two.
|
||||
For arrays, callbacks usually pass the parameters _`(value, index, array)`_, where _`value`_ is the value iterated to,
|
||||
_`index`_ is the current index, and _`array`_ is the array. For collections, you would have _`(value, key, collection)`_.
|
||||
Here, _`value`_ is the same, but _`key`_ is the key of the value, and _`collection`_ is the collection itself instead.
|
||||
|
||||
Methods that follow this philosophy of staying close to the _`Array`_ interface are as follows:
|
||||
|
||||
- _`find`_
|
||||
- _`filter`_ - Note that this returns a _`Collection`_ rather than an _`Array`_.
|
||||
- _`map`_ - Yet this returns an _`Array`_ of values instead of a _`Collection`_!
|
||||
- _`every`_
|
||||
- _`some`_
|
||||
- _`reduce`_
|
||||
- _`concat`_
|
||||
- _`sort`_
|
||||
|
||||
## Converting to Array
|
||||
|
||||
Since _`Collection`_ extends _`Map`_, it is an [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols), and can be converted to an _`Array`_ through either _`Array.from()`_ or spread syntax (_`...collection`_).
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
// For values.
|
||||
Array.from(collection.values());
|
||||
[...collection.values()];
|
||||
|
||||
// For keys.
|
||||
Array.from(collection.keys());
|
||||
[...collection.keys()];
|
||||
|
||||
// For [key, value] pairs.
|
||||
Array.from(collection);
|
||||
[...collection];
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
Many people convert Collections to Arrays way too much! This can lead to unnecessary and confusing code. Before you
|
||||
use _`Array.from()`_ or similar, ask yourself if whatever you are trying to do can't be done with the given _`Map`_ or
|
||||
_`Collection`_ methods or with a for-of loop.
|
||||
</Alert>
|
||||
|
||||
## Extra Utilities
|
||||
|
||||
Some methods are not from _`Array`_ and are instead entirely new to standard JavaScript.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
// A random value.
|
||||
collection.random();
|
||||
|
||||
// The first value.
|
||||
collection.first();
|
||||
|
||||
// The first 5 values.
|
||||
collection.first(5);
|
||||
|
||||
// Similar to `first`, but from the end.
|
||||
collection.last();
|
||||
collection.last(2);
|
||||
|
||||
// Removes anything that meets the condition from the collection.
|
||||
// Sort of like `filter`, but in-place.
|
||||
collection.sweep((user) => user.username === 'Bob');
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
A more complicated method is _`partition`_, which splits a single Collection into two new Collections based on the provided function.
|
||||
You can think of it as two \_`filter`\_s, but done at the same time:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
```js
|
||||
// `bots` is a Collection of users where their `bot` property was true.
|
||||
// `humans` is a Collection where the property was false instead!
|
||||
const [bots, humans] = collection.partition((u) => u.bot);
|
||||
|
||||
// Both return true.
|
||||
bots.every((b) => b.bot);
|
||||
humans.every((h) => !h.bot);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
@@ -19,13 +19,15 @@ import { ResultingCode } from '../../components/ResultingCode.jsx';
|
||||
<DiscordMessage
|
||||
interaction={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
command: 'ping',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -42,15 +44,15 @@ This section will cover only the bare minimum to get you started, but you can re
|
||||
|
||||
### Command deployment script
|
||||
|
||||
Create a `deploy-commands.js` file in your project directory. This file will be used to register and update the slash commands for your bot application.
|
||||
Create a _`deploy-commands.js`_ file in your project directory. This file will be used to register and update the slash commands for your bot application.
|
||||
|
||||
Since commands only need to be registered once, and updated when the definition (description, options etc) is changed, it's not necessary to connect a whole client to the gateway or do this on every `ready` event. As such, a standalone script using the lighter REST manager is preferred.
|
||||
Since commands only need to be registered once, and updated when the definition (description, options etc) is changed, it's not necessary to connect a whole client to the gateway or do this on every _`ready`_ event. As such, a standalone script using the lighter REST manager is preferred.
|
||||
|
||||
Below is a deployment script you can use. Focus on these variables:
|
||||
|
||||
- `clientId`: Your application's client id
|
||||
- `guildId`: Your development server's id
|
||||
- `commands`: An array of commands to register. The [slash command builder](/popular-topics/builders.md#slash-command-builders) from `discord.js` is used to build the data for your commands
|
||||
- _`clientId`_: Your application's client id
|
||||
- _`guildId`_: Your development server's id
|
||||
- _`commands`_: An array of commands to register. The [slash command builder](/popular-topics/builders.md#slash-command-builders) from _`discord.js`_ is used to build the data for your commands
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
In order to get your application's client id, go to [Discord Developer
|
||||
@@ -92,7 +94,7 @@ rest
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Once you fill in these values, run `node deploy-commands.js` in your project directory to register your commands to a single guild. It's also possible to [register commands globally](/interactions/slash-commands.md#global-commands).
|
||||
Once you fill in these values, run _`node deploy-commands.js`_ in your project directory to register your commands to a single guild. It's also possible to [register commands globally](/interactions/slash-commands.md#global-commands).
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
You only need to run `node deploy-commands.js` once. You should only run it again if you add or edit existing
|
||||
@@ -101,9 +103,9 @@ Once you fill in these values, run `node deploy-commands.js` in your project dir
|
||||
|
||||
## Replying to commands
|
||||
|
||||
Once you've registered your commands, you can listen for interactions via <DocsLink path="class/Client?scrollTo=e-interactionCreate" /> in your `index.js` file.
|
||||
Once you've registered your commands, you can listen for interactions via <DocsLink path="class/Client?scrollTo=e-interactionCreate" /> in your _`index.js`_ file.
|
||||
|
||||
You should first check if an interaction is a chat input command via <DocsLink path="class/Interaction?scrollTo=isChatInputCommand" type="method">`.isChatInputCommand()`</DocsLink>, and then check the <DocsLink path="class/CommandInteraction?scrollTo=commandName">`.commandName`</DocsLink> property to know which command it is. You can respond to interactions with <DocsLink path="class/CommandInteraction?scrollTo=reply">`.reply()`</DocsLink>.
|
||||
You should first check if an interaction is a chat input command via <DocsLink path="class/Interaction?scrollTo=isChatInputCommand" type="method">_`.isChatInputCommand()`_</DocsLink>, and then check the <DocsLink path="class/CommandInteraction?scrollTo=commandName">_`.commandName`_</DocsLink> property to know which command it is. You can respond to interactions with <DocsLink path="class/CommandInteraction?scrollTo=reply">_`.reply()`_</DocsLink>.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -130,7 +132,7 @@ client.login(token);
|
||||
|
||||
### Server info command
|
||||
|
||||
Note that servers are referred to as "guilds" in the Discord API and discord.js library. `interaction.guild` refers to the guild the interaction was sent in (a <DocsLink path="class/Guild" /> instance), which exposes properties such as `.name` or `.memberCount`.
|
||||
Note that servers are referred to as "guilds" in the Discord API and discord.js library. _`interaction.guild`_ refers to the guild the interaction was sent in (a <DocsLink path="class/Guild" /> instance), which exposes properties such as _`.name`_ or _`.memberCount`_.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -154,13 +156,15 @@ client.on('interactionCreate', async (interaction) => {
|
||||
<DiscordMessage
|
||||
interaction={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
command: 'server',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -170,7 +174,7 @@ client.on('interactionCreate', async (interaction) => {
|
||||
</DiscordMessage>
|
||||
</DiscordMessages>
|
||||
|
||||
You could also display the date the server was created, or the server's verification level. You would do those in the same manner – use `interaction.guild.createdAt` or `interaction.guild.verificationLevel`, respectively.
|
||||
You could also display the date the server was created, or the server's verification level. You would do those in the same manner – use _`interaction.guild.createdAt`_ or _`interaction.guild.verificationLevel`_, respectively.
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
Refer to the <DocsLink path="class/Guild" /> documentation for a list of all the available properties and methods!
|
||||
@@ -178,7 +182,7 @@ You could also display the date the server was created, or the server's verifica
|
||||
|
||||
### User info command
|
||||
|
||||
A "user" refers to a Discord user. `interaction.user` refers to the user the interaction was sent by (a <DocsLink path="class/User" /> instance), which exposes properties such as `.tag` or `.id`.
|
||||
A "user" refers to a Discord user. _`interaction.user`_ refers to the user the interaction was sent by (a <DocsLink path="class/User" /> instance), which exposes properties such as _`.tag`_ or _`.id`_.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -202,13 +206,15 @@ client.on('interactionCreate', async (interaction) => {
|
||||
<DiscordMessage
|
||||
interaction={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
command: 'user',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -224,9 +230,9 @@ client.on('interactionCreate', async (interaction) => {
|
||||
|
||||
And there you have it!
|
||||
|
||||
## The problem with `if`/`else if`
|
||||
## The problem with if/else if
|
||||
|
||||
If you don't plan on making more than a couple commands, then using an `if`/`else if` chain is fine; however, this isn't always the case. Using a giant `if`/`else if` chain will only hinder your development process in the long run.
|
||||
If you don't plan on making more than a couple commands, then using an _`if`_/_`else if`_ chain is fine; however, this isn't always the case. Using a giant _`if`_/_`else if`_ chain will only hinder your development process in the long run.
|
||||
|
||||
Here's a small list of reasons why you shouldn't do so:
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ Once you [add your bot to a server](/preparations/adding-your-bot-to-servers.md)
|
||||
|
||||
## Creating configuration files
|
||||
|
||||
As explained in the ["What is a token, anyway?"](/preparations/setting-up-a-bot-application.md#what-is-a-token-anyway) section, your token is essentially your bot's password, and you should protect it as best as possible. This can be done through a `config.json` file or by using environment variables.
|
||||
As explained in the ["What is a token, anyway?"](/preparations/setting-up-a-bot-application.md#what-is-a-token-anyway) section, your token is essentially your bot's password, and you should protect it as best as possible. This can be done through a _`config.json`_ file or by using environment variables.
|
||||
|
||||
Open your application in the [Discord Developer Portal](https://discord.com/developers/applications) and go to the "Bot" page to copy your token.
|
||||
|
||||
### Using `config.json`
|
||||
### Using config.json
|
||||
|
||||
Storing data in a `config.json` file is a common way of keeping your sensitive values safe. Create a `config.json` file in your project directory and paste in your token. You can access your token inside other files by using `require()`.
|
||||
Storing data in a _`config.json`_ file is a common way of keeping your sensitive values safe. Create a _`config.json`_ file in your project directory and paste in your token. You can access your token inside other files by using _`require()`_.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -43,16 +43,16 @@ console.log(token);
|
||||
|
||||
<Alert title="Caution" type="danger">
|
||||
If you're using Git, you should not commit this file and should [ignore it via
|
||||
`.gitignore`](/creating-your-bot/#git-and-gitignore).
|
||||
_`.gitignore`_](/creating-your-bot/#git-and-gitignore).
|
||||
</Alert>
|
||||
|
||||
### Using environment variables
|
||||
|
||||
Environment variables are special values for your environment (e.g., terminal session, Docker container, or environment variable file). You can pass these values into your code's scope so that you can use them.
|
||||
|
||||
One way to pass in environment variables is via the command line interface. When starting your app, instead of `node index.js`, use `TOKEN=your-token-goes-here node index.js`. You can repeat this pattern to expose other values as well.
|
||||
One way to pass in environment variables is via the command line interface. When starting your app, instead of _`node index.js`_, use _`TOKEN=your-token-goes-here node index.js`_. You can repeat this pattern to expose other values as well.
|
||||
|
||||
You can access the set values in your code via the `process.env` global variable, accessible in any file. Note that values passed this way will always be strings and that you might need to parse them to a number, if using them to do calculations.
|
||||
You can access the set values in your code via the _`process.env`_ global variable, accessible in any file. Note that values passed this way will always be strings and that you might need to parse them to a number, if using them to do calculations.
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -72,9 +72,9 @@ console.log(process.env.DISCORD_TOKEN);
|
||||
|
||||
#### Using dotenv
|
||||
|
||||
Another common approach is storing these values in a `.env` file. This spares you from always copying your token into the command line. Each line in a `.env` file should hold a `KEY=value` pair.
|
||||
Another common approach is storing these values in a _`.env`_ file. This spares you from always copying your token into the command line. Each line in a _`.env`_ file should hold a _`KEY=value`_ pair.
|
||||
|
||||
You can use the [`dotenv` package](https://www.npmjs.com/package/dotenv) for this. Once installed, require and use the package to load your `.env` file and attach the variables to `process.env`:
|
||||
You can use the [_`dotenv`_ package](https://www.npmjs.com/package/dotenv) for this. Once installed, require and use the package to load your _`.env`_ file and attach the variables to _`process.env`_:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -114,7 +114,7 @@ console.log(process.env.DISCORD_TOKEN);
|
||||
|
||||
<Alert title="Caution" type="danger">
|
||||
If you're using Git, you should not commit this file and should [ignore it via
|
||||
`.gitignore`](/creating-your-bot/#git-and-gitignore).
|
||||
_`.gitignore`_](/creating-your-bot/#git-and-gitignore).
|
||||
</Alert>
|
||||
|
||||
<Section client:load title="Online editors (Glitch, Heroku, Replit, etc.)" defaultClosed padded background gutter>
|
||||
@@ -126,11 +126,11 @@ While we generally do not recommend using online editors as hosting solutions, b
|
||||
|
||||
</Section>
|
||||
|
||||
### Git and `.gitignore`
|
||||
### Git and .gitignore
|
||||
|
||||
Git is a fantastic tool to keep track of your code changes and allows you to upload progress to services like [GitHub](https://github.com/), [GitLab](https://about.gitlab.com/), or [Bitbucket](https://bitbucket.org/product). While this is super useful to share code with other developers, it also bears the risk of uploading your configuration files with sensitive values!
|
||||
|
||||
You can specify files that Git should ignore in its versioning systems with a `.gitignore` file. Create a `.gitignore` file in your project directory and add the names of the files and folders you want to ignore:
|
||||
You can specify files that Git should ignore in its versioning systems with a _`.gitignore`_ file. Create a _`.gitignore`_ file in your project directory and add the names of the files and folders you want to ignore:
|
||||
|
||||
<CH.Code client:load>
|
||||
|
||||
@@ -143,15 +143,15 @@ config.json
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
Aside from keeping credentials safe, `node_modules` should be included here. Since this directory can be restored
|
||||
based on the entries in your `package.json` and `package-lock.json` files by running `npm install`, it does not need
|
||||
to be included in Git. You can specify quite intricate patterns in `.gitignore` files, check out the [Git
|
||||
documentation on `.gitignore`](https://git-scm.com/docs/gitignore) for more information!
|
||||
Aside from keeping credentials safe, _`node_modules`_ should be included here. Since this directory can be restored
|
||||
based on the entries in your _`package.json`_ and _`package-lock.json`_ files by running _`npm install`_, it does not
|
||||
need to be included in Git. You can specify quite intricate patterns in _`.gitignore`_ files, check out the [Git
|
||||
documentation on _`.gitignore`_](https://git-scm.com/docs/gitignore) for more information!
|
||||
</Alert>
|
||||
|
||||
## Creating the main file
|
||||
|
||||
Open your code editor and create a new file. We suggest that you save the file as `index.js`, but you may name it whatever you wish.
|
||||
Open your code editor and create a new file. We suggest that you save the file as _`index.js`_, but you may name it whatever you wish.
|
||||
|
||||
Here's the base code to get you started:
|
||||
|
||||
@@ -176,16 +176,16 @@ client.login(token);
|
||||
|
||||
</CH.Code>
|
||||
|
||||
This is how you create a client instance for your Discord bot and login to Discord. The `GatewayIntentBits.Guilds` intents option is necessary for your client to work properly, as it ensures that the caches for guilds, channels and roles are populated and available for internal use.
|
||||
This is how you create a client instance for your Discord bot and login to Discord. The _`GatewayIntentBits.Guilds`_ intents option is necessary for your client to work properly, as it ensures that the caches for guilds, channels and roles are populated and available for internal use.
|
||||
|
||||
Intents also define which events Discord should send to your bot, and you may wish to enable more than just the minimum. You can read more about the other intents on the [Intents topic](/popular-topics/intents).
|
||||
|
||||
Open your terminal and run `node index.js` to start the process. If you see "Ready!" after a few seconds, you're good to go!
|
||||
Open your terminal and run _`node index.js`_ to start the process. If you see "Ready!" after a few seconds, you're good to go!
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
You can open your `package.json` file and edit the `"main": "index.js"` field to point to your main file. You can then
|
||||
run `node .` in your terminal to start the process! After closing the process with `Ctrl + C`, you can press the up
|
||||
arrow on your keyboard to bring up the latest commands you've run. Pressing up and then enter after closing the
|
||||
You can open your _`package.json`_ file and edit the _`"main": "index.js"`_ field to point to your main file. You can
|
||||
then run _`node .`_ in your terminal to start the process! After closing the process with _`Ctrl + C`_, you can press
|
||||
the up arrow on your keyboard to bring up the latest commands you've run. Pressing up and then enter after closing the
|
||||
process is a quick way to start it up again.
|
||||
</Alert>
|
||||
|
||||
|
||||
@@ -9,13 +9,15 @@ import { DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs
|
||||
<DiscordMessage
|
||||
reply={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
content: 'Test',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -24,7 +26,8 @@ import { DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs
|
||||
</DiscordMessage>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -39,13 +42,15 @@ import { DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs
|
||||
<DiscordMessage
|
||||
reply={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
content: 'Test',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -58,13 +63,15 @@ import { DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs
|
||||
<DiscordMessage
|
||||
reply={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
content: 'Test',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
time: 'Today at 21:00',
|
||||
}}
|
||||
@@ -72,7 +79,8 @@ import { DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs
|
||||
<>
|
||||
<DiscordMessageEmbed
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
}}
|
||||
/>
|
||||
@@ -80,7 +88,8 @@ import { DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs
|
||||
<DiscordMessageEmbed footer={{ content: 'Footer' }} />
|
||||
<DiscordMessageEmbed
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
}}
|
||||
title={{ title: 'Title' }}
|
||||
|
||||
@@ -4,6 +4,7 @@ title: What's new
|
||||
category: Home
|
||||
---
|
||||
|
||||
import { CH } from '@code-hike/mdx/components';
|
||||
import { DiscordMessages, DiscordMessage } from '@discordjs/ui';
|
||||
|
||||
# What's new
|
||||
@@ -12,7 +13,8 @@ import { DiscordMessages, DiscordMessage } from '@discordjs/ui';
|
||||
<DiscordMessage
|
||||
interaction={{
|
||||
author: {
|
||||
avatar: 'https://cdn.discordapp.com/avatars/81440962496172032/81c0df2befe565b05018da6b026babb0.webp?size=160',
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
},
|
||||
command: 'upgrade',
|
||||
@@ -53,10 +55,10 @@ All content has been updated to use discord.js v14 syntax. The v13 version of th
|
||||
### Updated
|
||||
|
||||
- Commando: Replaced with [Sapphire](https://sapphirejs.dev/docs/Guide/getting-started/getting-started-with-sapphire)
|
||||
- [Voice](/voice/): Rewritten to use the [`@discordjs/voice`](https://github.com/discordjs/discord.js/tree/main/packages/voice) package
|
||||
- [Voice](/voice/): Rewritten to use the [_`@discordjs/voice`_](https://github.com/discordjs/discord.js/tree/main/packages/voice) package
|
||||
- [Command handling](/creating-your-bot/command-handling.md/): Updated to use slash commands
|
||||
- Obsolete sections removed
|
||||
- `client.on('message')` snippets updated to `client.on('interactionCreate')`
|
||||
- _`client.on('message')`_ snippets updated to _`client.on('interactionCreate')`_
|
||||
- [Message content will become a new privileged intent on August 31, 2022](https://support-dev.discord.com/hc/en-us/articles/4404772028055)
|
||||
|
||||
<DiscordMessages rounded>
|
||||
|
||||
3
apps/website/.gitignore
vendored
3
apps/website/.gitignore
vendored
@@ -19,12 +19,13 @@ dist/
|
||||
typings/
|
||||
.cache/
|
||||
build/
|
||||
api/
|
||||
src/styles/unocss.css
|
||||
.next/
|
||||
src/assets/readme/
|
||||
|
||||
# Miscellaneous
|
||||
.tmp/
|
||||
coverage/
|
||||
.vercel
|
||||
public/searchIndex
|
||||
.vscode
|
||||
|
||||
@@ -13,3 +13,4 @@ api/
|
||||
.next/
|
||||
.vercel/
|
||||
.cache/
|
||||
public/searchIndex
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
module.exports = require('../../.prettierrc.json');
|
||||
module.exports = {
|
||||
...require('../../.prettierrc.json'),
|
||||
plugins: [
|
||||
'prettier-plugin-tailwindcss', // MUST come last
|
||||
],
|
||||
pluginSearchDirs: false,
|
||||
};
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
/* eslint-disable tsdoc/syntax */
|
||||
import { URL, fileURLToPath } from 'node:url';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import bundleAnalyzer from '@next/bundle-analyzer';
|
||||
|
||||
const withBundleAnalyzer = bundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
*/
|
||||
export default {
|
||||
export default withBundleAnalyzer({
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
// Until Next.js fixes their type issues
|
||||
typescript: {
|
||||
ignoreBuildErrors: true,
|
||||
},
|
||||
cleanDistDir: true,
|
||||
outputFileTracing: true,
|
||||
experimental: {
|
||||
appDir: true,
|
||||
serverComponentsExternalPackages: ['@microsoft/api-extractor-model', 'jju', 'shiki'],
|
||||
outputFileTracingRoot: fileURLToPath(new URL('../../', import.meta.url)),
|
||||
fallbackNodePolyfills: false,
|
||||
},
|
||||
@@ -19,4 +30,4 @@ export default {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;",
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -5,17 +5,20 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"build:local": "yarn run --top-level docs --force && cross-env NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
|
||||
"build:prod": "yarn workspace @discordjs/api-extractor-utils run build && yarn workspace @discordjs/scripts run build && yarn workspace @discordjs/ui run build && yarn build:css && yarn build:next",
|
||||
"build:copy_readme": "cpy '../../packages/*/README.md' 'src/assets/readme' --rename='home-{{basename}}'",
|
||||
"build:local": "yarn run --top-level docs --force && yarn build:copy_readme && cross-env-shell NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
|
||||
"build:prod": "yarn workspaces foreach -ptR run build && yarn build:copy_readme && yarn build:css && yarn build:next",
|
||||
"build:next": "next build",
|
||||
"build:css": "yarn generate:css",
|
||||
"build:search_indicies": "yarn node scripts/generateAllIndicies.js",
|
||||
"build:analyze": "yarn run --top-level docs --force && cross-env-shell ANALYZE=true NEXT_PUBLIC_LOCAL_DEV=true yarn build:prod",
|
||||
"dev": "yarn run --top-level docs && concurrently 'yarn dev:css' 'yarn dev:next'",
|
||||
"dev:next": "next dev",
|
||||
"dev:css": "yarn generate:css --watch",
|
||||
"generate:css": "unocss 'src/**/*.tsx' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../packages/ui/unocss.config.ts",
|
||||
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx",
|
||||
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --fix"
|
||||
"generate:css": "unocss 'src/**/*.tsx' '../../packages/ui/src/lib/components/**/*.tsx' --out-file ./src/styles/unocss.css --config ../../unocss.config.ts",
|
||||
"lint": "prettier --check . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --format=pretty",
|
||||
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx --fix --format=pretty",
|
||||
"fmt": "yarn format"
|
||||
},
|
||||
"type": "module",
|
||||
"contributors": [
|
||||
@@ -45,51 +48,54 @@
|
||||
"@discordjs/ui": "workspace:^",
|
||||
"@microsoft/api-extractor-model": "7.24.0",
|
||||
"@microsoft/tsdoc": "0.14.1",
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@vercel/og": "^0.0.21",
|
||||
"@vscode/codicons": "^0.0.32",
|
||||
"ariakit": "^2.0.0-next.41",
|
||||
"cmdk": "^0.1.20",
|
||||
"meilisearch": "^0.28.0",
|
||||
"next": "^12.3.1",
|
||||
"next-mdx-remote": "^4.1.0",
|
||||
"next-progress": "^2.2.0",
|
||||
"next-themes": "^0.2.1",
|
||||
"meilisearch": "^0.30.0",
|
||||
"next": "^13.0.7-canary.1",
|
||||
"next-mdx-remote": "^4.2.0",
|
||||
"next-themes": "npm:@wits/next-themes@latest",
|
||||
"react": "^18.2.0",
|
||||
"react-custom-scrollbars-2": "^4.5.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.4.0",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-use": "^17.4.0",
|
||||
"rehype-ignore": "^1.0.1",
|
||||
"rehype-pretty-code": "^0.3.2",
|
||||
"rehype-ignore": "^1.0.3",
|
||||
"rehype-pretty-code": "^0.5.1",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-slug": "^5.0.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"sharp": "^0.31.1",
|
||||
"sharp": "^0.31.2",
|
||||
"shiki": "^0.11.1",
|
||||
"swr": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "^13.0.6",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/node": "^16.11.64",
|
||||
"@types/react": "^18.0.21",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/node": "16.18.4",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-syntax-highlighter": "^15.5.5",
|
||||
"@unocss/cli": "^0.45.29",
|
||||
"@unocss/reset": "^0.45.29",
|
||||
"@vitejs/plugin-react": "^2.1.0",
|
||||
"@vitest/coverage-c8": "^0.24.0",
|
||||
"concurrently": "^7.4.0",
|
||||
"@unocss/cli": "^0.47.5",
|
||||
"@unocss/reset": "^0.47.5",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"@vitest/coverage-c8": "^0.25.3",
|
||||
"concurrently": "^7.6.0",
|
||||
"cpy-cli": "^4.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-config-neon": "^0.1.37",
|
||||
"happy-dom": "^7.4.0",
|
||||
"prettier": "^2.7.1",
|
||||
"prettier-plugin-tailwindcss": "^0.1.13",
|
||||
"typescript": "^4.8.4",
|
||||
"unocss": "^0.45.29",
|
||||
"vercel": "^28.4.8",
|
||||
"vitest": "^0.24.0"
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-neon": "^0.1.40",
|
||||
"eslint-formatter-pretty": "^4.1.0",
|
||||
"happy-dom": "^7.7.0",
|
||||
"prettier": "^2.8.0",
|
||||
"prettier-plugin-tailwindcss": "^0.2.0",
|
||||
"typescript": "^4.9.3",
|
||||
"unocss": "^0.47.5",
|
||||
"vercel": "^28.7.0",
|
||||
"vitest": "^0.25.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
|
||||
BIN
apps/website/public/fonts/Inter-Black.woff2
Normal file
BIN
apps/website/public/fonts/Inter-Black.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-BlackItalic.woff2
Normal file
BIN
apps/website/public/fonts/Inter-BlackItalic.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-Bold.woff2
Normal file
BIN
apps/website/public/fonts/Inter-Bold.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-BoldItalic.woff2
Normal file
BIN
apps/website/public/fonts/Inter-BoldItalic.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-Italic.woff2
Normal file
BIN
apps/website/public/fonts/Inter-Italic.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-Light.woff2
Normal file
BIN
apps/website/public/fonts/Inter-Light.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-LightItalic.woff2
Normal file
BIN
apps/website/public/fonts/Inter-LightItalic.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-Medium.woff2
Normal file
BIN
apps/website/public/fonts/Inter-Medium.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-MediumItalic.woff2
Normal file
BIN
apps/website/public/fonts/Inter-MediumItalic.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-Regular.woff2
Normal file
BIN
apps/website/public/fonts/Inter-Regular.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-SemiBold.woff2
Normal file
BIN
apps/website/public/fonts/Inter-SemiBold.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-SemiBoldItalic.woff2
Normal file
BIN
apps/website/public/fonts/Inter-SemiBoldItalic.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-italic.var.woff2
Normal file
BIN
apps/website/public/fonts/Inter-italic.var.woff2
Normal file
Binary file not shown.
BIN
apps/website/public/fonts/Inter-roman.var.woff2
Normal file
BIN
apps/website/public/fonts/Inter-roman.var.woff2
Normal file
Binary file not shown.
@@ -0,0 +1,14 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { Providers } from './providers';
|
||||
import { CmdKDialog } from '~/components/CmdK';
|
||||
import { Header } from '~/components/Header';
|
||||
|
||||
export default function SidebarLayout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<Providers>
|
||||
<Header />
|
||||
<>{children}</>
|
||||
<CmdKDialog />
|
||||
</Providers>
|
||||
);
|
||||
}
|
||||
382
apps/website/src/app/docs/packages/[package]/[...slug]/page.tsx
Normal file
382
apps/website/src/app/docs/packages/[package]/[...slug]/page.tsx
Normal file
@@ -0,0 +1,382 @@
|
||||
/* eslint-disable no-case-declarations */
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
// eslint-disable-next-line n/prefer-global/process
|
||||
import process, { cwd } from 'node:process';
|
||||
import {
|
||||
findPackage,
|
||||
getMembers,
|
||||
type ApiItemJSON,
|
||||
type ApiClassJSON,
|
||||
type ApiFunctionJSON,
|
||||
type ApiInterfaceJSON,
|
||||
type ApiTypeAliasJSON,
|
||||
type ApiVariableJSON,
|
||||
type ApiEnumJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { createApiModel } from '@discordjs/scripts';
|
||||
import { ApiFunction, ApiItemKind, type ApiPackage } from '@microsoft/api-extractor-model';
|
||||
import Image from 'next/image';
|
||||
// import Head from 'next/head';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { serialize } from 'next-mdx-remote/serialize';
|
||||
import rehypeIgnore from 'rehype-ignore';
|
||||
import rehypePrettyCode, { type Options } from 'rehype-pretty-code';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { getHighlighter } from 'shiki';
|
||||
import shikiLangJavascript from 'shiki/languages/javascript.tmLanguage.json';
|
||||
import shikiLangTypescript from 'shiki/languages/typescript.tmLanguage.json';
|
||||
import shikiThemeDarkPlus from 'shiki/themes/dark-plus.json';
|
||||
import shikiThemeLightPlus from 'shiki/themes/light-plus.json';
|
||||
import vercelLogo from '../../../../../assets/powered-by-vercel.svg';
|
||||
import { MDXRemote } from '~/components/MDXRemote';
|
||||
import { Nav } from '~/components/Nav';
|
||||
import { Class } from '~/components/model/Class';
|
||||
import { Enum } from '~/components/model/Enum';
|
||||
import { Function } from '~/components/model/Function';
|
||||
import { Interface } from '~/components/model/Interface';
|
||||
import { TypeAlias } from '~/components/model/TypeAlias';
|
||||
import { Variable } from '~/components/model/Variable';
|
||||
import { MemberProvider } from '~/contexts/member';
|
||||
import { DESCRIPTION, PACKAGES } from '~/util/constants';
|
||||
import { findMember, findMemberByKey } from '~/util/model.server';
|
||||
import { tryResolveDescription } from '~/util/summary';
|
||||
|
||||
export async function generateStaticParams({ params }: { params?: { package: string } }) {
|
||||
const packageName = params?.package ?? 'builders';
|
||||
|
||||
try {
|
||||
let data: any[] = [];
|
||||
let versions: string[] = [];
|
||||
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
|
||||
const res = await readFile(join(cwd(), '..', '..', 'packages', packageName, 'docs', 'docs.api.json'), 'utf8');
|
||||
data = JSON.parse(res);
|
||||
} else {
|
||||
const response = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`, {
|
||||
next: { revalidate: 3_600 },
|
||||
});
|
||||
versions = await response.json();
|
||||
versions = versions.slice(-2);
|
||||
|
||||
for (const version of versions) {
|
||||
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${version}.api.json`);
|
||||
data = [...data, await res.json()];
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
const models = data.map((innerData) => createApiModel(innerData));
|
||||
const pkgs = models.map((model) => findPackage(model, packageName)) as ApiPackage[];
|
||||
|
||||
return [
|
||||
...versions.map((version) => ({ slug: [version] })),
|
||||
...pkgs.flatMap((pkg, idx) =>
|
||||
getMembers(pkg, versions[idx] ?? 'main').map((member) => {
|
||||
if (member.kind === ApiItemKind.Function && member.overloadIndex && member.overloadIndex > 1) {
|
||||
return {
|
||||
slug: [versions[idx] ?? 'main', `${member.name}:${member.overloadIndex}:${member.kind}`],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
slug: [versions[idx] ?? 'main', `${member.name}:${member.kind}`],
|
||||
};
|
||||
}),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
const model = createApiModel(data);
|
||||
const pkg = findPackage(model, packageName)!;
|
||||
|
||||
return [
|
||||
{ slug: ['main'] },
|
||||
...getMembers(pkg, 'main').map((member) => {
|
||||
if (member.kind === ApiItemKind.Function && member.overloadIndex && member.overloadIndex > 1) {
|
||||
return {
|
||||
slug: ['main', `${member.name}:${member.overloadIndex}:${member.kind}`],
|
||||
};
|
||||
}
|
||||
|
||||
return { slug: ['main', `${member.name}:${member.kind}`] };
|
||||
}),
|
||||
];
|
||||
} catch {
|
||||
return [{ slug: ['main'] }];
|
||||
}
|
||||
}
|
||||
|
||||
async function getData(packageName: string, slug: string[]) {
|
||||
const [branchName = 'main', member] = slug;
|
||||
|
||||
if (!PACKAGES.includes(packageName)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
let data;
|
||||
try {
|
||||
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
|
||||
const res = await readFile(join(cwd(), '..', '..', 'packages', packageName, 'docs', 'docs.api.json'), 'utf8');
|
||||
data = JSON.parse(res);
|
||||
} else {
|
||||
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${branchName}.api.json`);
|
||||
data = await res.json();
|
||||
}
|
||||
} catch {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const [memberName, overloadIndex] = member?.split('%3A') ?? [];
|
||||
|
||||
const readme = await readFile(join(cwd(), 'src', 'assets', 'readme', packageName, 'home-README.md'), 'utf8');
|
||||
|
||||
const mdxSource = await serialize(readme, {
|
||||
mdxOptions: {
|
||||
remarkPlugins: [remarkGfm],
|
||||
remarkRehypeOptions: { allowDangerousHtml: true },
|
||||
rehypePlugins: [
|
||||
rehypeRaw,
|
||||
rehypeIgnore,
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypePrettyCode,
|
||||
{
|
||||
theme: {
|
||||
dark: shikiThemeDarkPlus,
|
||||
light: shikiThemeLightPlus,
|
||||
},
|
||||
getHighlighter: async (options?: Partial<Options>) =>
|
||||
getHighlighter({
|
||||
...options,
|
||||
langs: [
|
||||
// @ts-expect-error: Working as intended
|
||||
{ id: 'javascript', aliases: ['js'], scopeName: 'source.js', grammar: shikiLangJavascript },
|
||||
// @ts-expect-error: Working as intended
|
||||
{ id: 'typescript', aliases: ['ts'], scopeName: 'source.ts', grammar: shikiLangTypescript },
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
],
|
||||
format: 'md',
|
||||
},
|
||||
});
|
||||
|
||||
const model = createApiModel(data);
|
||||
const pkg = findPackage(model, packageName);
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { containerKey, name } = findMember(model, packageName, memberName, branchName) ?? {};
|
||||
if (name && overloadIndex && !Number.isNaN(Number.parseInt(overloadIndex, 10))) {
|
||||
containerKey = ApiFunction.getContainerKey(name, Number.parseInt(overloadIndex, 10));
|
||||
}
|
||||
|
||||
const members = pkg
|
||||
? getMembers(pkg, branchName).filter((item) => item.overloadIndex === null || item.overloadIndex <= 1)
|
||||
: [];
|
||||
const foundMember =
|
||||
memberName && containerKey ? findMemberByKey(model, packageName, containerKey, branchName) ?? null : null;
|
||||
const description = foundMember ? tryResolveDescription(foundMember) ?? DESCRIPTION : DESCRIPTION;
|
||||
|
||||
return {
|
||||
packageName,
|
||||
branchName,
|
||||
data: {
|
||||
members,
|
||||
member: foundMember,
|
||||
description,
|
||||
source: mdxSource,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// function resolveMember(packageName?: string | undefined, member?: SidebarLayoutProps['data']['member']) {
|
||||
// switch (member?.kind) {
|
||||
// case 'Class': {
|
||||
// const typedMember = member as ApiClassJSON;
|
||||
// return `?pkg=${packageName}&kind=${typedMember.kind}&name=${typedMember.name}&methods=${typedMember.methods.length}&props=${typedMember.properties.length}`;
|
||||
// }
|
||||
|
||||
// case 'Function': {
|
||||
// const typedMember = member as ApiFunctionJSON;
|
||||
// return `?pkg=${packageName}&kind=${typedMember.kind}&name=${typedMember.name}`;
|
||||
// }
|
||||
|
||||
// case 'Interface': {
|
||||
// const typedMember = member as ApiInterfaceJSON;
|
||||
// return `?pkg=${packageName}&kind=${typedMember.kind}&name=${typedMember.name}&methods=${typedMember.methods.length}&props=${typedMember.properties.length}`;
|
||||
// }
|
||||
|
||||
// case 'TypeAlias': {
|
||||
// const typedMember = member as ApiTypeAliasJSON;
|
||||
// return `?pkg=${packageName}&kind=${typedMember.kind}&name=${typedMember.name}`;
|
||||
// }
|
||||
|
||||
// case 'Variable': {
|
||||
// const typedMember = member as ApiVariableJSON;
|
||||
// return `?pkg=${packageName}&kind=${typedMember.kind}&name=${typedMember.name}`;
|
||||
// }
|
||||
|
||||
// case 'Enum': {
|
||||
// const typedMember = member as ApiEnumJSON;
|
||||
// return `?pkg=${packageName}&kind=${typedMember.kind}&name=${typedMember.name}&members=${typedMember.members.length}`;
|
||||
// }
|
||||
|
||||
// default: {
|
||||
// return `?pkg=${packageName}&kind=${member?.kind}&name=${member?.name}`;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
function member(props?: ApiItemJSON | undefined) {
|
||||
switch (props?.kind) {
|
||||
case 'Class':
|
||||
return <Class data={props as ApiClassJSON} />;
|
||||
case 'Function':
|
||||
return <Function data={props as ApiFunctionJSON} key={props.containerKey} />;
|
||||
case 'Interface':
|
||||
return <Interface data={props as ApiInterfaceJSON} />;
|
||||
case 'TypeAlias':
|
||||
return <TypeAlias data={props as ApiTypeAliasJSON} />;
|
||||
case 'Variable':
|
||||
return <Variable data={props as ApiVariableJSON} />;
|
||||
case 'Enum':
|
||||
return <Enum data={props as ApiEnumJSON} />;
|
||||
default:
|
||||
return <div>Cannot render that item type</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page({ params }: { params: { package: string; slug: string[] } }) {
|
||||
const data = await getData(params.package, params.slug);
|
||||
|
||||
// const name = useMemo(
|
||||
// () => `discord.js${params.data?.member?.name ? ` | ${params.data.member.name}` : ''}`,
|
||||
// [params.data?.member?.name],
|
||||
// );
|
||||
// const ogTitle = useMemo(
|
||||
// () => `${params.packageName ?? 'discord.js'}${params.data?.member?.name ? ` | ${params.data.member.name}` : ''}`,
|
||||
// [params.packageName, params.data?.member?.name],
|
||||
// );
|
||||
// const ogImage = useMemo(
|
||||
// () => resolveMember(params.packageName, params.data?.member),
|
||||
// [params.packageName, params.data?.member],
|
||||
// );
|
||||
|
||||
// Just in case
|
||||
// return <iframe src="https://discord.js.org" style={{ border: 0, height: '100%', width: '100%' }}></iframe>;
|
||||
|
||||
return (
|
||||
<MemberProvider member={data.data?.member}>
|
||||
<Nav members={data.data.members} />
|
||||
<>
|
||||
{/* <Head>
|
||||
<title key="title">{name}</title>
|
||||
<meta content={params.data.description} key="description" name="description" />
|
||||
<meta content={ogTitle} key="og_title" property="og:title" />
|
||||
<meta content={params.data.description} key="og_description" property="og:description" />
|
||||
<meta content={`https://discordjs.dev/api/og_model${ogImage}`} key="og_image" property="og:image" />
|
||||
</Head> */}
|
||||
<main
|
||||
className={`pt-18 lg:pl-76 ${
|
||||
(data?.data.member?.kind === 'Class' || data?.data.member?.kind === 'Interface') &&
|
||||
((data.data.member as ApiClassJSON | ApiInterfaceJSON).methods?.length ||
|
||||
(data.data.member as ApiClassJSON | ApiInterfaceJSON).properties?.length)
|
||||
? 'xl:pr-64'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<article className="dark:bg-dark-600 bg-light-600">
|
||||
<div className="dark:bg-dark-800 relative z-10 min-h-[calc(100vh_-_70px)] bg-white p-6 pb-20 shadow">
|
||||
{data.data?.member ? (
|
||||
member(data.data.member)
|
||||
) : data.data?.source ? (
|
||||
<div className="prose max-w-none">
|
||||
<MDXRemote {...data.data?.source} />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="h-76 md:h-52" />
|
||||
<footer
|
||||
className={`dark:bg-dark-600 h-76 lg:pl-84 bg-light-600 fixed bottom-0 left-0 right-0 md:h-52 md:pl-4 md:pr-16 ${
|
||||
(data?.data.member?.kind === 'Class' || data?.data.member?.kind === 'Interface') &&
|
||||
((data.data.member as ApiClassJSON | ApiInterfaceJSON).methods?.length ||
|
||||
(data.data.member as ApiClassJSON | ApiInterfaceJSON).properties?.length)
|
||||
? 'xl:pr-76'
|
||||
: 'xl:pr-16'
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto flex max-w-6xl flex-col place-items-center gap-12 pt-12 lg:place-content-center">
|
||||
<div className="flex w-full flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
title="Vercel"
|
||||
>
|
||||
<Image alt="Vercel" src={vercelLogo} />
|
||||
</a>
|
||||
<div className="flex flex-row gap-6 md:gap-12">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-lg font-semibold">Community</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://discord.gg/djs"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Discord
|
||||
</a>
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://github.com/discordjs/discord.js/discussions"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub discussions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-lg font-semibold">Project</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord.js
|
||||
</a>
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://discordjs.guide"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord.js guide
|
||||
</a>
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://discord-api-types.dev"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord-api-types
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
</>
|
||||
</MemberProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
'use client';
|
||||
|
||||
// import { ThemeProvider } from 'next-themes';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { CmdKProvider } from '~/contexts/cmdK';
|
||||
import { NavProvider } from '~/contexts/nav';
|
||||
|
||||
export function Providers({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<NavProvider>
|
||||
<CmdKProvider>
|
||||
{/* <ThemeProvider
|
||||
attribute="class"
|
||||
cookieName="theme"
|
||||
defaultTheme="system"
|
||||
disableTransitionOnChange
|
||||
value={{
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
}}
|
||||
> */}
|
||||
{children}
|
||||
{/* </ThemeProvider> */}
|
||||
</CmdKProvider>
|
||||
</NavProvider>
|
||||
);
|
||||
}
|
||||
10
apps/website/src/app/docs/packages/[package]/layout.tsx
Normal file
10
apps/website/src/app/docs/packages/[package]/layout.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return PACKAGES.map((packageName) => ({ package: packageName }));
|
||||
}
|
||||
|
||||
export default function PackageLayout({ children }: PropsWithChildren) {
|
||||
return children;
|
||||
}
|
||||
54
apps/website/src/app/docs/packages/[package]/page.tsx
Normal file
54
apps/website/src/app/docs/packages/[package]/page.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { VscArrowLeft } from '@react-icons/all-files/vsc/VscArrowLeft';
|
||||
import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
|
||||
import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
|
||||
import Link from 'next/link';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
async function getData(pkg: string) {
|
||||
if (!PACKAGES.includes(pkg)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${pkg}`, { next: { revalidate: 3_600 } });
|
||||
const data: string[] = await res.json();
|
||||
|
||||
if (!data.length) {
|
||||
throw new Error('Failed to fetch data');
|
||||
}
|
||||
|
||||
return data.reverse();
|
||||
}
|
||||
|
||||
export default async function Page({ params }: { params: { package: string } }) {
|
||||
const data = await getData(params.package);
|
||||
|
||||
return (
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
<div className="flex grow flex-col place-content-center gap-4">
|
||||
<h1 className="text-2xl font-semibold">Select a version:</h1>
|
||||
{data.map((version) => (
|
||||
<Link
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||
href={`/docs/packages/${params.package}/${version}`}
|
||||
key={version}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscVersions size={25} />
|
||||
<h2 className="font-semibold">{version}</h2>
|
||||
</div>
|
||||
<VscArrowRight size={20} />
|
||||
</div>
|
||||
</Link>
|
||||
)) ?? null}
|
||||
<Link
|
||||
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
|
||||
href="/docs/packages"
|
||||
>
|
||||
<VscArrowLeft size={20} /> Go back
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
92
apps/website/src/app/docs/packages/page.tsx
Normal file
92
apps/website/src/app/docs/packages/page.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
|
||||
import { VscArrowLeft } from '@react-icons/all-files/vsc/VscArrowLeft';
|
||||
import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
|
||||
import { VscPackage } from '@react-icons/all-files/vsc/VscPackage';
|
||||
import Link from 'next/link';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
async function getData() {
|
||||
return Promise.all(
|
||||
PACKAGES.map(async (pkg) => {
|
||||
const response = await fetch(`https://docs.discordjs.dev/api/info?package=${pkg}`, {
|
||||
next: { revalidate: 3_600 },
|
||||
});
|
||||
const versions = await response.json();
|
||||
const latestVersion = versions.at(-2) ?? 'main';
|
||||
return { packageName: pkg, version: latestVersion };
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export default async function Page() {
|
||||
const data = await getData();
|
||||
|
||||
const findLatestVersion = (pkg: string) => data.find((version) => version.packageName === pkg);
|
||||
|
||||
return (
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
<div className="flex grow flex-col place-content-center gap-4">
|
||||
<h1 className="text-2xl font-semibold">Select a package:</h1>
|
||||
<a
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-content-between rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||
href="https://discord.js.org/#/docs/discord.js"
|
||||
>
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscPackage size={25} />
|
||||
<h2 className="font-semibold">discord.js</h2>
|
||||
</div>
|
||||
</div>
|
||||
<VscArrowRight size={20} />
|
||||
</div>
|
||||
</a>
|
||||
{PACKAGES.map((pkg) => (
|
||||
<Link
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-between rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||
href={`/docs/packages/${pkg}/${findLatestVersion(pkg)?.version ?? 'main'}`}
|
||||
key={pkg}
|
||||
>
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscPackage size={25} />
|
||||
<h2 className="font-semibold">{pkg}</h2>
|
||||
</div>
|
||||
{/* <Link href={`/docs/packages/${pkg}`}>
|
||||
<div
|
||||
className="bg-blurple focus:ring-width-2 flex h-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center place-items-center rounded border-0 px-2 text-xs font-semibold leading-none text-white outline-0 focus:ring focus:ring-white active:translate-y-px"
|
||||
role="link"
|
||||
>
|
||||
Select version
|
||||
</div>
|
||||
</Link> */}
|
||||
</div>
|
||||
<VscArrowRight size={20} />
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
<a
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex h-11 transform-gpu cursor-pointer select-none appearance-none place-content-between rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||
href="https://discord-api-types.dev/"
|
||||
>
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex grow flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscPackage size={25} />
|
||||
<h2 className="font-semibold">discord-api-types</h2>
|
||||
</div>
|
||||
</div>
|
||||
<FiExternalLink size={20} />
|
||||
</div>
|
||||
</a>
|
||||
<Link
|
||||
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
|
||||
href="/"
|
||||
>
|
||||
<VscArrowLeft size={20} /> Go back
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
28
apps/website/src/app/head.tsx
Normal file
28
apps/website/src/app/head.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { DESCRIPTION } from '~/util/constants';
|
||||
|
||||
export default function Head() {
|
||||
return (
|
||||
<>
|
||||
<link href="/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180" />
|
||||
<link href="/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png" />
|
||||
<link href="/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png" />
|
||||
<link href="/site.webmanifest" rel="manifest" />
|
||||
<link color="#090a16" href="/safari-pinned-tab.svg" rel="mask-icon" />
|
||||
<meta content="light dark" name="color-scheme" />
|
||||
<meta content="discord.js" name="apple-mobile-web-app-title" />
|
||||
<meta content="discord.js" name="application-name" />
|
||||
<meta content="#090a16" name="msapplication-TileColor" />
|
||||
<meta content={DESCRIPTION} key="description" name="description" />
|
||||
<meta content="discord.js" property="og:site_name" />
|
||||
<meta content="website" property="og:type" />
|
||||
<meta content="discord.js" key="og_title" property="og:title" />
|
||||
<meta content={DESCRIPTION} key="og_description" property="og:description" />
|
||||
<meta content="https://discordjs.dev/api/og" key="og_image" property="og:image" />
|
||||
<meta content="summary_large_image" name="twitter:card" />
|
||||
<meta content="@iCrawlToGo" name="twitter:creator" />
|
||||
<title key="title">discord.js</title>
|
||||
<meta content="minimum-scale=1, initial-scale=1, width=device-width" name="viewport" />
|
||||
<meta content="#5865f2" name="theme-color" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
27
apps/website/src/app/layout.tsx
Normal file
27
apps/website/src/app/layout.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { ServerThemeProvider } from 'next-themes';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
import '@unocss/reset/tailwind.css';
|
||||
import '../styles/inter.css';
|
||||
import '../styles/unocss.css';
|
||||
import '../styles/cmdk.css';
|
||||
import '../styles/main.css';
|
||||
|
||||
export default function RootLayout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<ServerThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
disableTransitionOnChange
|
||||
value={{
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
}}
|
||||
>
|
||||
<html lang="en">
|
||||
<head />
|
||||
<body className="dark:bg-dark-800 bg-white">{children}</body>
|
||||
</html>
|
||||
</ServerThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,20 +1,21 @@
|
||||
import Head from 'next/head';
|
||||
// import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function FourOhFourPage() {
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
{/* <Head>
|
||||
<title key="title">discord.js | 404</title>
|
||||
<meta content="discord.js | 404" key="og_title" property="og:title" />
|
||||
</Head>
|
||||
</Head> */}
|
||||
<div className="mx-auto flex h-full max-w-lg flex-col place-content-center place-items-center gap-8 py-16 px-8 lg:py-0 lg:px-6">
|
||||
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
|
||||
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
|
||||
<Link href="/docs/packages" prefetch={false}>
|
||||
<a className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px">
|
||||
Take me back
|
||||
</a>
|
||||
<Link
|
||||
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
|
||||
href="/docs/packages"
|
||||
>
|
||||
Take me back
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
@@ -1,11 +1,11 @@
|
||||
import Image from 'next/future/image';
|
||||
import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { FiExternalLink } from 'react-icons/fi';
|
||||
import vercelLogo from '../assets/powered-by-vercel.svg';
|
||||
import { SyntaxHighlighter } from '~/components/SyntaxHighlighter';
|
||||
import { CODE_EXAMPLE } from '~/util/constants';
|
||||
|
||||
export default function IndexRoute() {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="mx-auto flex max-w-6xl flex-col place-items-center gap-12 py-16 px-8 lg:h-full lg:place-content-center lg:py-0 lg:px-6">
|
||||
<div className="flex flex-col place-items-center gap-10 lg:flex-row lg:gap-6">
|
||||
@@ -20,13 +20,15 @@ export default function IndexRoute() {
|
||||
significantly tidier and easier to comprehend.
|
||||
</p>
|
||||
<div className="flex flex-row gap-4">
|
||||
<Link href="/docs" prefetch={false}>
|
||||
<a className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px">
|
||||
Docs
|
||||
</a>
|
||||
<Link
|
||||
className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 px-6 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px"
|
||||
href="/docs"
|
||||
prefetch={false}
|
||||
>
|
||||
Docs
|
||||
</Link>
|
||||
<a
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 focus:ring-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 rounded border bg-transparent px-4 text-base font-semibold leading-none text-black no-underline outline-0 focus:ring active:translate-y-px dark:text-white"
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 focus:ring-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center gap-2 rounded border bg-transparent px-4 text-base font-semibold leading-none text-black no-underline outline-0 focus:ring active:translate-y-px dark:text-white"
|
||||
href="https://discordjs.guide"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
@@ -34,7 +36,7 @@ export default function IndexRoute() {
|
||||
Guide <FiExternalLink />
|
||||
</a>
|
||||
<a
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 focus:ring-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none appearance-none place-items-center gap-2 rounded border bg-transparent px-4 text-base font-semibold leading-none text-black no-underline outline-0 focus:ring active:translate-y-px dark:text-white"
|
||||
className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 border-light-900 hover:bg-light-200 active:bg-light-300 focus:ring-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none appearance-none flex-row place-items-center gap-2 rounded border bg-transparent px-4 text-base font-semibold leading-none text-black no-underline outline-0 focus:ring active:translate-y-px dark:text-white"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
@@ -45,7 +47,7 @@ export default function IndexRoute() {
|
||||
</div>
|
||||
<SyntaxHighlighter code={CODE_EXAMPLE} />
|
||||
</div>
|
||||
<div className="flex place-content-center">
|
||||
<div className="flex flex-row place-content-center">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||
BIN
apps/website/src/assets/fonts/Inter-Black.ttf
Normal file
BIN
apps/website/src/assets/fonts/Inter-Black.ttf
Normal file
Binary file not shown.
BIN
apps/website/src/assets/fonts/Inter-Bold.ttf
Normal file
BIN
apps/website/src/assets/fonts/Inter-Bold.ttf
Normal file
Binary file not shown.
BIN
apps/website/src/assets/fonts/Inter-Light.ttf
Normal file
BIN
apps/website/src/assets/fonts/Inter-Light.ttf
Normal file
Binary file not shown.
BIN
apps/website/src/assets/fonts/Inter-Medium.ttf
Normal file
BIN
apps/website/src/assets/fonts/Inter-Medium.ttf
Normal file
Binary file not shown.
BIN
apps/website/src/assets/fonts/Inter-Regular.ttf
Normal file
BIN
apps/website/src/assets/fonts/Inter-Regular.ttf
Normal file
Binary file not shown.
BIN
apps/website/src/assets/fonts/Inter-SemiBold.ttf
Normal file
BIN
apps/website/src/assets/fonts/Inter-SemiBold.ttf
Normal file
Binary file not shown.
@@ -1,23 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiItemKind } from '@microsoft/api-extractor-model';
|
||||
import { VscArrowRight } from '@react-icons/all-files/vsc/VscArrowRight';
|
||||
import { VscSymbolClass } from '@react-icons/all-files/vsc/VscSymbolClass';
|
||||
import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
|
||||
import { VscSymbolField } from '@react-icons/all-files/vsc/VscSymbolField';
|
||||
import { VscSymbolInterface } from '@react-icons/all-files/vsc/VscSymbolInterface';
|
||||
import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
|
||||
import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
|
||||
import { VscSymbolVariable } from '@react-icons/all-files/vsc/VscSymbolVariable';
|
||||
import { Dialog } from 'ariakit/dialog';
|
||||
import { Command } from 'cmdk';
|
||||
import { useRouter } from 'next/router';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
VscArrowRight,
|
||||
VscSymbolClass,
|
||||
VscSymbolEnum,
|
||||
VscSymbolField,
|
||||
VscSymbolInterface,
|
||||
VscSymbolMethod,
|
||||
VscSymbolProperty,
|
||||
VscSymbolVariable,
|
||||
} from 'react-icons/vsc';
|
||||
import { useKey } from 'react-use';
|
||||
import { useCmdK } from '~/contexts/cmdK';
|
||||
import { client } from '~/util/search';
|
||||
|
||||
function resolveIcon(item: keyof ApiItemKind) {
|
||||
function resolveIcon(item: keyof typeof ApiItemKind) {
|
||||
switch (item) {
|
||||
case 'Class':
|
||||
return <VscSymbolClass className="shrink-0" size={25} />;
|
||||
@@ -29,33 +29,31 @@ function resolveIcon(item: keyof ApiItemKind) {
|
||||
return <VscSymbolProperty className="shrink-0" size={25} />;
|
||||
case 'TypeAlias':
|
||||
return <VscSymbolField className="shrink-0" size={25} />;
|
||||
case 'Variables':
|
||||
case 'Variable':
|
||||
return <VscSymbolVariable className="shrink-0" size={25} />;
|
||||
default:
|
||||
return <VscSymbolMethod className="shrink-0" size={25} />;
|
||||
}
|
||||
}
|
||||
|
||||
export function CmdKDialog({
|
||||
currentPackageName,
|
||||
currentVersion,
|
||||
}: {
|
||||
currentPackageName?: string | undefined;
|
||||
currentVersion?: string | undefined;
|
||||
}) {
|
||||
export function CmdKDialog() {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const dialog = useCmdK();
|
||||
const [search, setSearch] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<any[]>([]);
|
||||
|
||||
const packageName = pathname?.split('/').slice(3, 4)[0];
|
||||
const branchName = pathname?.split('/').slice(4, 5)[0];
|
||||
|
||||
const searchResultItems = useMemo(
|
||||
() =>
|
||||
searchResults?.map((item) => (
|
||||
<Command.Item
|
||||
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-width-4 [&[aria-selected]]:ring my-1 flex flex transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white"
|
||||
className="dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 [&[aria-selected]]:ring-blurple [&[aria-selected]]:ring-width-4 my-1 flex transform-gpu cursor-pointer select-none appearance-none flex-row place-content-center rounded bg-transparent px-4 py-2 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 active:translate-y-px active:bg-neutral-200 dark:text-white [&[aria-selected]]:ring"
|
||||
key={item.id}
|
||||
onSelect={() => {
|
||||
void router.push(item.path);
|
||||
router.push(item.path);
|
||||
dialog!.setOpen(false);
|
||||
}}
|
||||
>
|
||||
@@ -101,12 +99,12 @@ export function CmdKDialog({
|
||||
|
||||
useEffect(() => {
|
||||
const searchDoc = async (searchString: string, version: string) => {
|
||||
const res = await client.index(`${currentPackageName}-${version}`).search(searchString, { limit: 5 });
|
||||
const res = await client.index(`${packageName}-${version}`).search(searchString, { limit: 5 });
|
||||
setSearchResults(res.hits);
|
||||
};
|
||||
|
||||
if (search && currentPackageName) {
|
||||
void searchDoc(search, currentVersion?.replaceAll('.', '-') ?? 'main');
|
||||
if (search && packageName) {
|
||||
void searchDoc(search, branchName?.replaceAll('.', '-') ?? 'main');
|
||||
} else {
|
||||
setSearchResults([]);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import type { TokenDocumentation, ApiItemJSON, AnyDocNodeJSON, InheritanceData } from '@discordjs/api-extractor-utils';
|
||||
import { FiLink } from '@react-icons/all-files/fi/FiLink';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { FiLink } from 'react-icons/fi';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { InheritanceText } from './InheritanceText';
|
||||
import { TSDoc } from './tsdoc/TSDoc';
|
||||
@@ -34,7 +36,7 @@ export function CodeListing({
|
||||
}>) {
|
||||
return (
|
||||
<div className="scroll-mt-30 flex flex-col gap-4" id={name}>
|
||||
<div className="md:-ml-8.5 flex flex-col gap-0.5 md:flex-row md:place-items-center md:gap-2">
|
||||
<div className="md:-ml-8.5 flex flex-col gap-2 md:flex-row md:place-items-center">
|
||||
<a
|
||||
aria-label="Anchor"
|
||||
className="focus:ring-width-2 focus:ring-blurple hidden rounded outline-0 focus:ring md:inline-block"
|
||||
@@ -45,17 +47,17 @@ export function CodeListing({
|
||||
{deprecation || readonly || optional ? (
|
||||
<div className="flex flex-row gap-1">
|
||||
{deprecation ? (
|
||||
<div className="flex h-5 place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="flex h-5 flex-row place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Deprecated
|
||||
</div>
|
||||
) : null}
|
||||
{readonly ? (
|
||||
<div className="bg-blurple flex h-5 place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Readonly
|
||||
</div>
|
||||
) : null}
|
||||
{optional ? (
|
||||
<div className="bg-blurple flex h-5 place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Optional
|
||||
</div>
|
||||
) : null}
|
||||
@@ -73,7 +75,7 @@ export function CodeListing({
|
||||
</div>
|
||||
</div>
|
||||
{summary || inheritanceData ? (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="mb-4 flex flex-col gap-4">
|
||||
{deprecation ? <TSDoc node={deprecation} /> : null}
|
||||
{summary ? <TSDoc node={summary} /> : null}
|
||||
{comment ? <TSDoc node={comment} /> : null}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type {
|
||||
ApiItemJSON,
|
||||
TokenDocumentation,
|
||||
@@ -6,18 +8,16 @@ import type {
|
||||
ApiInterfaceJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { Section } from '@discordjs/ui';
|
||||
import type { ReactNode } from 'react';
|
||||
import { VscListSelection } from '@react-icons/all-files/vsc/VscListSelection';
|
||||
import { VscSymbolClass } from '@react-icons/all-files/vsc/VscSymbolClass';
|
||||
import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
|
||||
import { VscSymbolInterface } from '@react-icons/all-files/vsc/VscSymbolInterface';
|
||||
import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
|
||||
import { VscSymbolParameter } from '@react-icons/all-files/vsc/VscSymbolParameter';
|
||||
import { VscSymbolVariable } from '@react-icons/all-files/vsc/VscSymbolVariable';
|
||||
import { Fragment, type PropsWithChildren } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import {
|
||||
VscSymbolClass,
|
||||
VscSymbolMethod,
|
||||
VscSymbolEnum,
|
||||
VscSymbolInterface,
|
||||
VscSymbolVariable,
|
||||
VscListSelection,
|
||||
VscSymbolParameter,
|
||||
} from 'react-icons/vsc';
|
||||
import { useMedia } from 'react-use';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { SyntaxHighlighter } from './SyntaxHighlighter';
|
||||
|
||||
126
apps/website/src/components/Header.tsx
Normal file
126
apps/website/src/components/Header.tsx
Normal file
@@ -0,0 +1,126 @@
|
||||
'use client';
|
||||
|
||||
import { FiCommand } from '@react-icons/all-files/fi/FiCommand';
|
||||
// import { VscColorMode } from '@react-icons/all-files/vsc/VscColorMode';
|
||||
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
|
||||
import { VscMenu } from '@react-icons/all-files/vsc/VscMenu';
|
||||
import { VscSearch } from '@react-icons/all-files/vsc/VscSearch';
|
||||
import { Button } from 'ariakit/button';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
// import { useTheme } from 'next-themes';
|
||||
import { Fragment, useEffect, useMemo, useState } from 'react';
|
||||
import { useCmdK } from '~/contexts/cmdK';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
|
||||
export function Header() {
|
||||
const pathname = usePathname();
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const { setOpened } = useNav();
|
||||
// const { resolvedTheme, setTheme } = useTheme();
|
||||
const dialog = useCmdK();
|
||||
// const toggleTheme = () => setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
setAsPathWithoutQueryAndAnchor(pathname?.split('?')[0]?.split('#')[0] ?? '');
|
||||
}, [pathname]);
|
||||
|
||||
const asPathWithoutContainerKey = useMemo(
|
||||
() => asPathWithoutQueryAndAnchor?.split(':')[0] ?? '',
|
||||
[asPathWithoutQueryAndAnchor],
|
||||
);
|
||||
|
||||
const pathElements = useMemo(
|
||||
() =>
|
||||
asPathWithoutContainerKey
|
||||
.split('/')
|
||||
.slice(1)
|
||||
.map((path, idx, original) => (
|
||||
<Link
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 hover:underline focus:ring"
|
||||
href={`/${original.slice(0, idx + 1).join('/')}`}
|
||||
key={idx}
|
||||
>
|
||||
{path}
|
||||
</Link>
|
||||
)),
|
||||
[asPathWithoutContainerKey],
|
||||
);
|
||||
|
||||
const breadcrumbs = useMemo(
|
||||
() =>
|
||||
pathElements.flatMap((el, idx, array) => {
|
||||
if (idx === 0) {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<div className="mx-2">/</div>
|
||||
{el}
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (idx !== array.length - 1) {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
{el}
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return <Fragment key={idx}>{el}</Fragment>;
|
||||
}),
|
||||
[pathElements],
|
||||
);
|
||||
|
||||
return (
|
||||
<header className="dark:bg-dark-600 dark:border-dark-100 bg-light-600 border-light-800 fixed top-0 left-0 z-20 w-full border-b">
|
||||
<div className="h-18 block px-6">
|
||||
<div className="flex h-full flex-row place-content-between place-items-center">
|
||||
<Button
|
||||
aria-label="Menu"
|
||||
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px lg:hidden"
|
||||
onClick={() => setOpened((open) => !open)}
|
||||
>
|
||||
<VscMenu size={24} />
|
||||
</Button>
|
||||
<div className="hidden md:flex md:flex-row md:overflow-hidden">{breadcrumbs}</div>
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<Button
|
||||
as="div"
|
||||
className="dark:bg-dark-800 focus:ring-width-2 focus:ring-blurple rounded bg-white px-4 py-2.5 outline-0 focus:ring"
|
||||
onClick={() => dialog?.toggle()}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<VscSearch size={18} />
|
||||
<span className="opacity-65">Search...</span>
|
||||
<div className="md:opacity-65 hidden md:flex md:flex-row md:place-items-center md:gap-2">
|
||||
<FiCommand size={18} /> K
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="GitHub"
|
||||
as="a"
|
||||
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded rounded-full border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<VscGithubInverted size={24} />
|
||||
</Button>
|
||||
{/* <Button
|
||||
aria-label="Toggle theme"
|
||||
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none flex-row place-items-center rounded-full rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||
onClick={() => toggleTheme()}
|
||||
>
|
||||
<VscColorMode size={24} />
|
||||
</Button> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { TokenDocumentation } from '@discordjs/api-extractor-utils';
|
||||
import Link from 'next/link';
|
||||
|
||||
@@ -7,10 +9,12 @@ export function HyperlinkedText({ tokens }: { tokens: TokenDocumentation[] }) {
|
||||
{tokens.map((token, idx) => {
|
||||
if (token.path) {
|
||||
return (
|
||||
<Link href={token.path} key={idx} prefetch={false}>
|
||||
<a className="text-blurple focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring">
|
||||
{token.text}
|
||||
</a>
|
||||
<Link
|
||||
className="text-blurple focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href={token.path}
|
||||
key={idx}
|
||||
>
|
||||
{token.text}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { InheritanceData } from '@discordjs/api-extractor-utils';
|
||||
import Link from 'next/link';
|
||||
|
||||
@@ -5,10 +7,11 @@ export function InheritanceText({ data }: { data: InheritanceData }) {
|
||||
return (
|
||||
<span className="font-semibold">
|
||||
Inherited from{' '}
|
||||
<Link href={data.path} prefetch={false}>
|
||||
<a className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring">
|
||||
{data.parentName}
|
||||
</a>
|
||||
<Link
|
||||
className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring"
|
||||
href={data.path}
|
||||
>
|
||||
{data.parentName}
|
||||
</Link>
|
||||
</span>
|
||||
);
|
||||
|
||||
3
apps/website/src/components/MDXRemote.tsx
Normal file
3
apps/website/src/components/MDXRemote.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
'use client';
|
||||
|
||||
export { MDXRemote } from 'next-mdx-remote';
|
||||
@@ -1,8 +1,11 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiMethodJSON, ApiMethodSignatureJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit';
|
||||
import { FiLink } from '@react-icons/all-files/fi/FiLink';
|
||||
import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
|
||||
import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { FiLink } from 'react-icons/fi';
|
||||
import { VscChevronDown, VscVersions } from 'react-icons/vsc';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
import { InheritanceText } from './InheritanceText';
|
||||
import { ParameterTable } from './ParameterTable';
|
||||
@@ -47,17 +50,17 @@ export function MethodItem({ data }: { data: ApiMethodJSON | ApiMethodSignatureJ
|
||||
(data.kind === 'Method' && method.static) ? (
|
||||
<div className="flex flex-row gap-1">
|
||||
{data.deprecated ? (
|
||||
<div className="flex h-5 place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="flex h-5 flex-row place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Deprecated
|
||||
</div>
|
||||
) : null}
|
||||
{data.kind === 'Method' && method.protected ? (
|
||||
<div className="bg-blurple flex h-5 place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Protected
|
||||
</div>
|
||||
) : null}
|
||||
{data.kind === 'Method' && method.static ? (
|
||||
<div className="bg-blurple flex h-5 place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Static
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiMethodJSON, ApiMethodSignatureJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import { MethodItem } from './MethodItem';
|
||||
|
||||
37
apps/website/src/components/Nav.tsx
Normal file
37
apps/website/src/components/Nav.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
'use client';
|
||||
|
||||
import type { getMembers } from '@discordjs/api-extractor-utils';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { PackageSelect } from './PackageSelect';
|
||||
import { SidebarItems } from './SidebarItems';
|
||||
import { VersionSelect } from './VersionSelect';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
|
||||
export function Nav({ members }: { members: ReturnType<typeof getMembers> }) {
|
||||
// eslint-disable-next-line @typescript-eslint/unbound-method
|
||||
const { opened } = useNav();
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[73px] left-0 bottom-0 z-20 h-[calc(100vh_-_73px)] w-full border-r bg-white ${
|
||||
opened ? 'block' : 'hidden'
|
||||
} lg:w-76 lg:max-w-76 lg:block`}
|
||||
>
|
||||
<Scrollbars
|
||||
autoHide
|
||||
hideTracksWhenNotNeeded
|
||||
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
|
||||
renderTrackVertical={(props) => (
|
||||
<div {...props} className="absolute top-0.5 right-0.5 bottom-0.5 z-30 w-1.5 rounded" />
|
||||
)}
|
||||
universal
|
||||
>
|
||||
<div className="flex flex-col gap-3 px-3 pt-3">
|
||||
<PackageSelect />
|
||||
<VersionSelect />
|
||||
</div>
|
||||
<SidebarItems members={members} />
|
||||
</Scrollbars>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
68
apps/website/src/components/PackageSelect.tsx
Normal file
68
apps/website/src/components/PackageSelect.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
|
||||
import { VscPackage } from '@react-icons/all-files/vsc/VscPackage';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
export function PackageSelect() {
|
||||
const pathname = usePathname();
|
||||
const packageName = pathname?.split('/').slice(3, 4)[0];
|
||||
|
||||
const packageMenu = useMenuState({ gutter: 8, sameWidth: true, fitViewport: true });
|
||||
|
||||
const packageMenuItems = useMemo(
|
||||
() => [
|
||||
<a href="https://discord.js.org/#/docs/discord.js" key="discord.js">
|
||||
<MenuItem
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
|
||||
id="discord-js"
|
||||
onClick={() => packageMenu.setOpen(false)}
|
||||
state={packageMenu}
|
||||
>
|
||||
discord.js
|
||||
</MenuItem>
|
||||
</a>,
|
||||
...PACKAGES.map((pkg) => (
|
||||
<Link href={`/docs/packages/${pkg}/main`} key={pkg}>
|
||||
<MenuItem
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
|
||||
id={pkg}
|
||||
onClick={() => packageMenu.setOpen(false)}
|
||||
state={packageMenu}
|
||||
>
|
||||
{pkg}
|
||||
</MenuItem>
|
||||
</Link>
|
||||
)),
|
||||
],
|
||||
[packageMenu],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuButton
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
|
||||
state={packageMenu}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<VscPackage size={20} />
|
||||
<span className="font-semibold">{packageName}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${packageMenu.open ? 'rotate-180' : 'rotate-0'}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</MenuButton>
|
||||
<Menu
|
||||
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 focus:ring-width-2 focus:ring-blurple z-20 flex flex-col rounded border bg-white p-1 outline-0 focus:ring"
|
||||
state={packageMenu}
|
||||
>
|
||||
{packageMenuItems}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ParameterDocumentation } from '@discordjs/api-extractor-utils';
|
||||
import { useMemo } from 'react';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiPropertyItemJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import { CodeListing } from './CodeListing';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type {
|
||||
ApiClassJSON,
|
||||
ApiInterfaceJSON,
|
||||
@@ -5,8 +7,10 @@ import type {
|
||||
ApiConstructorJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { Section } from '@discordjs/ui';
|
||||
import { VscSymbolConstant } from '@react-icons/all-files/vsc/VscSymbolConstant';
|
||||
import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
|
||||
import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
|
||||
import { useMemo } from 'react';
|
||||
import { VscSymbolConstant, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
|
||||
import { useMedia } from 'react-use';
|
||||
import { MethodList } from './MethodList';
|
||||
import { ParameterTable } from './ParameterTable';
|
||||
@@ -66,12 +70,12 @@ export function ConstructorSection({ data }: { data: ApiConstructorJSON }) {
|
||||
{data.deprecated || data.protected ? (
|
||||
<div className="flex flex-row gap-1">
|
||||
{data.deprecated ? (
|
||||
<div className="flex h-5 place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="flex h-5 flex-row place-content-center place-items-center rounded-full bg-red-500 px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Deprecated
|
||||
</div>
|
||||
) : null}
|
||||
{data.protected ? (
|
||||
<div className="bg-blurple flex h-5 place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
<div className="bg-blurple flex h-5 flex-row place-content-center place-items-center rounded-full px-3 text-center text-xs font-semibold uppercase text-white">
|
||||
Protected
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
@@ -1,16 +1,28 @@
|
||||
'use client';
|
||||
|
||||
import type { getMembers } from '@discordjs/api-extractor-utils';
|
||||
import { Section } from '@discordjs/ui';
|
||||
import { VscSymbolClass } from '@react-icons/all-files/vsc/VscSymbolClass';
|
||||
import { VscSymbolEnum } from '@react-icons/all-files/vsc/VscSymbolEnum';
|
||||
import { VscSymbolField } from '@react-icons/all-files/vsc/VscSymbolField';
|
||||
import { VscSymbolInterface } from '@react-icons/all-files/vsc/VscSymbolInterface';
|
||||
import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
|
||||
import { VscSymbolVariable } from '@react-icons/all-files/vsc/VscSymbolVariable';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { type Dispatch, type SetStateAction, useEffect, useState, useMemo } from 'react';
|
||||
import {
|
||||
VscSymbolClass,
|
||||
VscSymbolEnum,
|
||||
VscSymbolInterface,
|
||||
VscSymbolField,
|
||||
VscSymbolVariable,
|
||||
VscSymbolMethod,
|
||||
} from 'react-icons/vsc';
|
||||
import type { GroupedMembers, Members } from './SidebarLayout';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
|
||||
type Members = ReturnType<typeof getMembers>;
|
||||
|
||||
interface GroupedMembers {
|
||||
Classes: Members;
|
||||
Enums: Members;
|
||||
Functions: Members;
|
||||
Interfaces: Members;
|
||||
Types: Members;
|
||||
Variables: Members;
|
||||
}
|
||||
|
||||
function groupMembers(members: Members): GroupedMembers {
|
||||
const Classes: Members = [];
|
||||
@@ -65,20 +77,16 @@ function resolveIcon(item: keyof GroupedMembers) {
|
||||
}
|
||||
}
|
||||
|
||||
export function SidebarItems({
|
||||
members,
|
||||
setOpened,
|
||||
}: {
|
||||
members: Members;
|
||||
setOpened: Dispatch<SetStateAction<boolean>>;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
export function SidebarItems({ members }: { members: Members }) {
|
||||
const pathname = usePathname();
|
||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||
const groupItems = useMemo(() => groupMembers(members), [members]);
|
||||
const { setOpened } = useNav();
|
||||
|
||||
useEffect(() => {
|
||||
setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0] ?? '');
|
||||
}, [router.asPath]);
|
||||
setAsPathWithoutQueryAndAnchor(pathname?.split('?')[0]?.split('#')[0] ?? '');
|
||||
}, [pathname]);
|
||||
|
||||
const groupItems = useMemo(() => groupMembers(members), [members]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 p-3 pb-32 lg:pb-12">
|
||||
@@ -87,23 +95,23 @@ export function SidebarItems({
|
||||
.map((group, idx) => (
|
||||
<Section icon={resolveIcon(group)} key={idx} title={group}>
|
||||
{groupItems[group].map((member, index) => (
|
||||
<Link href={member.path} key={index} prefetch={false}>
|
||||
<a
|
||||
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-0 focus:rounded focus:border-0 focus:ring ${
|
||||
asPathWithoutQueryAndAnchor === member.path
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
}`}
|
||||
onClick={() => setOpened(false)}
|
||||
title={member.name}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
|
||||
<span className="truncate">{member.name}</span>
|
||||
{member.overloadIndex && member.overloadIndex > 1 ? (
|
||||
<span className="text-xs">{member.overloadIndex}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</a>
|
||||
<Link
|
||||
className={`dark:border-dark-100 border-light-800 focus:ring-width-2 focus:ring-blurple ml-5 flex flex-col border-l p-[5px] pl-6 outline-0 focus:rounded focus:border-0 focus:ring ${
|
||||
asPathWithoutQueryAndAnchor === member.path
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
}`}
|
||||
href={member.path}
|
||||
key={index}
|
||||
onClick={() => setOpened(false)}
|
||||
title={member.name}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
|
||||
<span className="truncate">{member.name}</span>
|
||||
{member.overloadIndex && member.overloadIndex > 1 ? (
|
||||
<span className="text-xs">{member.overloadIndex}</span>
|
||||
) : null}
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</Section>
|
||||
|
||||
@@ -1,378 +0,0 @@
|
||||
import type { getMembers, ApiItemJSON, ApiClassJSON, ApiInterfaceJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Button } from 'ariakit/button';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
|
||||
import Image from 'next/future/image';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { MDXRemoteSerializeResult } from 'next-mdx-remote';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { type PropsWithChildren, useState, useEffect, useMemo, Fragment } from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { FiCommand } from 'react-icons/fi';
|
||||
import {
|
||||
VscChevronDown,
|
||||
VscColorMode,
|
||||
VscGithubInverted,
|
||||
VscMenu,
|
||||
VscPackage,
|
||||
VscSearch,
|
||||
VscVersions,
|
||||
} from 'react-icons/vsc';
|
||||
import { useMedia /* useLockBodyScroll */ } from 'react-use';
|
||||
import useSWR from 'swr';
|
||||
import vercelLogo from '../assets/powered-by-vercel.svg';
|
||||
import { CmdKDialog } from './CmdK';
|
||||
import { SidebarItems } from './SidebarItems';
|
||||
import { useCmdK } from '~/contexts/cmdK';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
import { fetcher } from '~/util/fetcher';
|
||||
import type { findMember } from '~/util/model.server';
|
||||
|
||||
export interface SidebarLayoutProps {
|
||||
branchName: string;
|
||||
data: {
|
||||
member: ReturnType<typeof findMember>;
|
||||
members: ReturnType<typeof getMembers>;
|
||||
searchIndex: any[];
|
||||
source: MDXRemoteSerializeResult;
|
||||
};
|
||||
packageName: string;
|
||||
|
||||
selectedMember?: ApiItemJSON | undefined;
|
||||
}
|
||||
|
||||
export type Members = SidebarLayoutProps['data']['members'];
|
||||
|
||||
export interface GroupedMembers {
|
||||
Classes: Members;
|
||||
Enums: Members;
|
||||
Functions: Members;
|
||||
Interfaces: Members;
|
||||
Types: Members;
|
||||
Variables: Members;
|
||||
}
|
||||
|
||||
export function SidebarLayout({
|
||||
packageName,
|
||||
branchName,
|
||||
data,
|
||||
children,
|
||||
}: PropsWithChildren<Partial<SidebarLayoutProps>>) {
|
||||
const router = useRouter();
|
||||
const dialog = useCmdK();
|
||||
const [asPathWithoutQueryAndAnchor, setAsPathWithoutQueryAndAnchor] = useState('');
|
||||
const { data: versions } = useSWR<string[]>(`https://docs.discordjs.dev/api/info?package=${packageName}`, fetcher);
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const toggleTheme = () => setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
||||
const matches = useMedia('(min-width: 992px)', false);
|
||||
const [opened, setOpened] = useState(false);
|
||||
const packageMenu = useMenuState({ gutter: 8, sameWidth: true, fitViewport: true });
|
||||
const versionMenu = useMenuState({ gutter: 8, sameWidth: true, fitViewport: true });
|
||||
// useLockBodyScroll(opened);
|
||||
|
||||
useEffect(() => {
|
||||
if (matches) {
|
||||
setOpened(false);
|
||||
}
|
||||
}, [matches]);
|
||||
|
||||
useEffect(() => {
|
||||
setAsPathWithoutQueryAndAnchor(router.asPath.split('?')[0]?.split('#')[0]?.split(':')[0] ?? '');
|
||||
}, [router.asPath]);
|
||||
|
||||
const packageMenuItems = useMemo(
|
||||
() => [
|
||||
<a href="https://discord.js.org/#/docs/discord.js" key="discord.js">
|
||||
<MenuItem
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
|
||||
onClick={() => packageMenu.setOpen(false)}
|
||||
state={packageMenu}
|
||||
>
|
||||
discord.js
|
||||
</MenuItem>
|
||||
</a>,
|
||||
...PACKAGES.map((pkg) => (
|
||||
<Link href={`/docs/packages/${pkg}/main`} key={pkg} passHref prefetch={false}>
|
||||
<MenuItem
|
||||
as="a"
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
|
||||
onClick={() => packageMenu.setOpen(false)}
|
||||
state={packageMenu}
|
||||
>
|
||||
{pkg}
|
||||
</MenuItem>
|
||||
</Link>
|
||||
)),
|
||||
],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[],
|
||||
);
|
||||
|
||||
const versionMenuItems = useMemo(
|
||||
() =>
|
||||
versions
|
||||
?.map((item) => (
|
||||
<Link href={`/docs/packages/${packageName}/${item}`} key={item} passHref prefetch={false}>
|
||||
<MenuItem
|
||||
as="a"
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
|
||||
onClick={() => versionMenu.setOpen(false)}
|
||||
state={versionMenu}
|
||||
>
|
||||
{item}
|
||||
</MenuItem>
|
||||
</Link>
|
||||
))
|
||||
.reverse() ?? [],
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[versions, packageName],
|
||||
);
|
||||
|
||||
const pathElements = useMemo(
|
||||
() =>
|
||||
asPathWithoutQueryAndAnchor
|
||||
.split('/')
|
||||
.slice(1)
|
||||
.map((path, idx, original) => (
|
||||
<Link href={`/${original.slice(0, idx + 1).join('/')}`} key={idx} prefetch={false}>
|
||||
<a className="focus:ring-width-2 focus:ring-blurple rounded outline-0 hover:underline focus:ring">{path}</a>
|
||||
</Link>
|
||||
)),
|
||||
[asPathWithoutQueryAndAnchor],
|
||||
);
|
||||
|
||||
const breadcrumbs = useMemo(
|
||||
() =>
|
||||
pathElements.flatMap((el, idx, array) => {
|
||||
if (idx === 0) {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<div className="mx-2">/</div>
|
||||
{el}
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (idx !== array.length - 1) {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
{el}
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return <Fragment key={idx}>{el}</Fragment>;
|
||||
}),
|
||||
[pathElements],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<header className="dark:bg-dark-600 dark:border-dark-100 bg-light-600 border-light-800 fixed top-0 left-0 z-20 w-full border-b">
|
||||
<div className="h-18 block px-6">
|
||||
<div className="flex h-full flex-row place-content-between place-items-center">
|
||||
<Button
|
||||
aria-label="Menu"
|
||||
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px lg:hidden"
|
||||
onClick={() => setOpened((open) => !open)}
|
||||
>
|
||||
<VscMenu size={24} />
|
||||
</Button>
|
||||
<div className="hidden md:flex md:flex-row">{breadcrumbs}</div>
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<Button
|
||||
as="div"
|
||||
className="dark:bg-dark-800 focus:ring-width-2 focus:ring-blurple rounded bg-white px-4 py-2.5 outline-0 focus:ring"
|
||||
onClick={() => dialog?.toggle()}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<VscSearch size={18} />
|
||||
<span className="opacity-65">Search...</span>
|
||||
<div className="opacity-65 flex flex-row place-items-center gap-2">
|
||||
<FiCommand size={18} /> K
|
||||
</div>
|
||||
</div>
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="GitHub"
|
||||
as="a"
|
||||
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded rounded-full border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<VscGithubInverted size={24} />
|
||||
</Button>
|
||||
<Button
|
||||
aria-label="Toggle theme"
|
||||
className="focus:ring-width-2 focus:ring-blurple flex h-6 w-6 transform-gpu cursor-pointer select-none appearance-none place-items-center rounded-full rounded border-0 bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-0 focus:ring active:translate-y-px"
|
||||
onClick={() => toggleTheme()}
|
||||
>
|
||||
<VscColorMode size={24} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<nav
|
||||
className={`dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[73px] left-0 bottom-0 z-20 h-[calc(100vh_-_73px)] w-full border-r bg-white ${
|
||||
opened ? 'block' : 'hidden'
|
||||
} lg:w-76 lg:max-w-76 lg:block`}
|
||||
>
|
||||
<Scrollbars
|
||||
autoHide
|
||||
hideTracksWhenNotNeeded
|
||||
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
|
||||
renderTrackVertical={(props) => (
|
||||
<div {...props} className="absolute top-0.5 right-0.5 bottom-0.5 z-30 w-1.5 rounded" />
|
||||
)}
|
||||
universal
|
||||
>
|
||||
<div className="flex flex-col gap-3 px-3 pt-3">
|
||||
<MenuButton
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
|
||||
state={packageMenu}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<VscPackage size={20} />
|
||||
<span className="font-semibold">{packageName}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${
|
||||
packageMenu.open ? 'rotate-180' : 'rotate-0'
|
||||
}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</MenuButton>
|
||||
<Menu
|
||||
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 focus:ring-width-2 focus:ring-blurple z-20 flex flex-col rounded border bg-white p-1 outline-0 focus:ring"
|
||||
state={packageMenu}
|
||||
>
|
||||
{packageMenuItems}
|
||||
</Menu>
|
||||
<MenuButton
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
|
||||
state={versionMenu}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<VscVersions size={20} />
|
||||
<span className="font-semibold">{branchName}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${
|
||||
versionMenu.open ? 'rotate-180' : 'rotate-0'
|
||||
}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</MenuButton>
|
||||
<Menu
|
||||
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 focus:ring-width-2 focus:ring-blurple z-20 flex flex-col rounded border bg-white p-1 outline-0 focus:ring"
|
||||
state={versionMenu}
|
||||
>
|
||||
{versionMenuItems}
|
||||
</Menu>
|
||||
</div>
|
||||
<SidebarItems members={data?.members ?? []} setOpened={setOpened} />
|
||||
</Scrollbars>
|
||||
</nav>
|
||||
<main
|
||||
className={`pt-18 lg:pl-76 ${
|
||||
(data?.member?.kind === 'Class' || data?.member?.kind === 'Interface') &&
|
||||
((data.member as ApiClassJSON | ApiInterfaceJSON).methods?.length ||
|
||||
(data.member as ApiClassJSON | ApiInterfaceJSON).properties?.length)
|
||||
? 'xl:pr-64'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
<article className="dark:bg-dark-600 bg-light-600">
|
||||
<div className="dark:bg-dark-800 relative z-10 min-h-[calc(100vh_-_70px)] bg-white p-6 pb-20 shadow">
|
||||
{children}
|
||||
</div>
|
||||
<div className="h-76 md:h-52" />
|
||||
<footer
|
||||
className={`dark:bg-dark-600 h-76 lg:pl-84 bg-light-600 fixed bottom-0 left-0 right-0 md:h-52 md:pl-4 md:pr-16 ${
|
||||
(data?.member?.kind === 'Class' || data?.member?.kind === 'Interface') &&
|
||||
((data.member as ApiClassJSON | ApiInterfaceJSON).methods?.length ||
|
||||
(data.member as ApiClassJSON | ApiInterfaceJSON).properties?.length)
|
||||
? 'xl:pr-76'
|
||||
: 'xl:pr-16'
|
||||
}`}
|
||||
>
|
||||
<div className="mx-auto flex max-w-6xl flex-col place-items-center gap-12 pt-12 lg:place-content-center">
|
||||
<div className="flex w-full flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
title="Vercel"
|
||||
>
|
||||
<Image alt="Vercel" src={vercelLogo} />
|
||||
</a>
|
||||
<div className="flex flex-row gap-6 md:gap-12">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-lg font-semibold">Community</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://discord.gg/djs"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Discord
|
||||
</a>
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://github.com/discordjs/discord.js/discussions"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
GitHub discussions
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="text-lg font-semibold">Project</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord.js
|
||||
</a>
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://discordjs.guide"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord.js guide
|
||||
</a>
|
||||
<a
|
||||
className="focus:ring-width-2 focus:ring-blurple rounded outline-0 focus:ring"
|
||||
href="https://discord-api-types.dev"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord-api-types
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
<CmdKDialog currentPackageName={packageName} currentVersion={branchName} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { PrismAsyncLight } from 'react-syntax-highlighter';
|
||||
import { vscDarkPlus, prism } from 'react-syntax-highlighter/dist/cjs/styles/prism';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo, type ReactNode } from 'react';
|
||||
|
||||
export function Table({
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiClassJSON, ApiInterfaceJSON } from '@discordjs/api-extractor-utils';
|
||||
import { VscListSelection } from '@react-icons/all-files/vsc/VscListSelection';
|
||||
import { VscSymbolMethod } from '@react-icons/all-files/vsc/VscSymbolMethod';
|
||||
import { VscSymbolProperty } from '@react-icons/all-files/vsc/VscSymbolProperty';
|
||||
import { useMemo } from 'react';
|
||||
import { VscListSelection, VscSymbolMethod, VscSymbolProperty } from 'react-icons/vsc';
|
||||
|
||||
export function TableOfContentItems({
|
||||
methods,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { TypeParameterData } from '@discordjs/api-extractor-utils';
|
||||
import { useMemo } from 'react';
|
||||
import { HyperlinkedText } from './HyperlinkedText';
|
||||
|
||||
61
apps/website/src/components/VersionSelect.tsx
Normal file
61
apps/website/src/components/VersionSelect.tsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
|
||||
import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
import { fetcher } from '~/util/fetcher';
|
||||
|
||||
export function VersionSelect() {
|
||||
const pathname = usePathname();
|
||||
const packageName = pathname?.split('/').slice(3, 4)[0];
|
||||
const branchName = pathname?.split('/').slice(4, 5)[0];
|
||||
|
||||
const { data: versions } = useSWR<string[]>(`https://docs.discordjs.dev/api/info?package=${packageName}`, fetcher);
|
||||
const versionMenu = useMenuState({ gutter: 8, sameWidth: true, fitViewport: true });
|
||||
|
||||
const versionMenuItems = useMemo(
|
||||
() =>
|
||||
versions
|
||||
?.map((item) => (
|
||||
<Link href={`/docs/packages/${packageName}/${item}`} key={item}>
|
||||
<MenuItem
|
||||
className="hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple my-0.5 rounded bg-white p-3 text-sm outline-0 focus:ring"
|
||||
onClick={() => versionMenu.setOpen(false)}
|
||||
state={versionMenu}
|
||||
>
|
||||
{item}
|
||||
</MenuItem>
|
||||
</Link>
|
||||
))
|
||||
.reverse() ?? [],
|
||||
[versions, packageName, versionMenu],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuButton
|
||||
className="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-600 dark:hover:bg-dark-500 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-0 focus:ring"
|
||||
state={versionMenu}
|
||||
>
|
||||
<div className="flex flex-row place-content-between place-items-center">
|
||||
<div className="flex flex-row place-items-center gap-3">
|
||||
<VscVersions size={20} />
|
||||
<span className="font-semibold">{branchName}</span>
|
||||
</div>
|
||||
<VscChevronDown
|
||||
className={`transform transition duration-150 ease-in-out ${versionMenu.open ? 'rotate-180' : 'rotate-0'}`}
|
||||
size={20}
|
||||
/>
|
||||
</div>
|
||||
</MenuButton>
|
||||
<Menu
|
||||
className="dark:bg-dark-600 border-light-800 dark:border-dark-100 focus:ring-width-2 focus:ring-blurple z-20 flex flex-col rounded border bg-white p-1 outline-0 focus:ring"
|
||||
state={versionMenu}
|
||||
>
|
||||
{versionMenuItems}
|
||||
</Menu>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiClassJSON } from '@discordjs/api-extractor-utils';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { ConstructorSection, MethodsSection, PropertiesSection } from '../Sections';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiEnumJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Section } from '@discordjs/ui';
|
||||
import { VscSymbolEnumMember } from 'react-icons/vsc';
|
||||
import { VscSymbolEnumMember } from '@react-icons/all-files/vsc/VscSymbolEnumMember';
|
||||
import { Fragment } from 'react';
|
||||
import { useMedia } from 'react-use';
|
||||
import { CodeListing, CodeListingSeparatorType } from '../CodeListing';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
@@ -13,13 +16,15 @@ export function Enum({ data }: { data: ApiEnumJSON }) {
|
||||
<Section dense={matches} icon={<VscSymbolEnumMember size={20} />} padded title="Members">
|
||||
<div className="flex flex-col gap-4">
|
||||
{data.members.map((member) => (
|
||||
<CodeListing
|
||||
key={member.name}
|
||||
name={member.name}
|
||||
separator={CodeListingSeparatorType.Value}
|
||||
summary={member.summary}
|
||||
typeTokens={member.initializerTokens}
|
||||
/>
|
||||
<Fragment key={member.name}>
|
||||
<CodeListing
|
||||
name={member.name}
|
||||
separator={CodeListingSeparatorType.Value}
|
||||
summary={member.summary}
|
||||
typeTokens={member.initializerTokens}
|
||||
/>
|
||||
<div className="border-light-900 dark:border-dark-100 -mx-8 border-t-2" />
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiFunctionJSON } from '@discordjs/api-extractor-utils';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit';
|
||||
import { VscChevronDown } from '@react-icons/all-files/vsc/VscChevronDown';
|
||||
import { VscVersions } from '@react-icons/all-files/vsc/VscVersions';
|
||||
import { Menu, MenuButton, MenuItem, useMenuState } from 'ariakit/menu';
|
||||
import { useState } from 'react';
|
||||
import { VscChevronDown, VscVersions } from 'react-icons/vsc';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { ParametersSection } from '../Sections';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiInterfaceJSON } from '@discordjs/api-extractor-utils';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
import { MethodsSection, PropertiesSection } from '../Sections';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiTypeAliasJSON } from '@discordjs/api-extractor-utils';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiVariableJSON } from '@discordjs/api-extractor-utils';
|
||||
import { DocContainer } from '../DocContainer';
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import { Alert } from '@discordjs/ui';
|
||||
import { StandardTags } from '@microsoft/tsdoc';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
'use client';
|
||||
|
||||
import type {
|
||||
AnyDocNodeJSON,
|
||||
DocPlainTextJSON,
|
||||
@@ -38,20 +40,24 @@ export function TSDoc({ node }: { node: AnyDocNodeJSON }): JSX.Element {
|
||||
|
||||
if (codeDestination) {
|
||||
return (
|
||||
<Link href={codeDestination.path} key={idx} prefetch={false}>
|
||||
<a className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring">
|
||||
{text ?? codeDestination.name}
|
||||
</a>
|
||||
<Link
|
||||
className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring"
|
||||
href={codeDestination.path}
|
||||
key={idx}
|
||||
>
|
||||
{text ?? codeDestination.name}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
if (urlDestination) {
|
||||
return (
|
||||
<Link href={urlDestination} key={idx} prefetch={false}>
|
||||
<a className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring">
|
||||
{text ?? urlDestination}
|
||||
</a>
|
||||
<Link
|
||||
className="text-blurple focus:ring-width-2 focus:ring-blurple rounded font-mono outline-0 focus:ring"
|
||||
href={urlDestination}
|
||||
key={idx}
|
||||
>
|
||||
{text ?? urlDestination}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { type DisclosureState, useDialogState } from 'ariakit';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { createContext, useContext } from 'react';
|
||||
'use client';
|
||||
|
||||
import { useDialogState } from 'ariakit/dialog';
|
||||
import type { DisclosureState } from 'ariakit/disclosure';
|
||||
import { type PropsWithChildren, createContext, useContext } from 'react';
|
||||
|
||||
export const CmdKContext = createContext<DisclosureState | null>(null);
|
||||
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import type { ApiItemJSON } from '@discordjs/api-extractor-utils';
|
||||
import { createContext, useContext, type ReactNode } from 'react';
|
||||
|
||||
export const MemberContext = createContext<ApiItemJSON | undefined>(undefined);
|
||||
export const MemberContext = createContext<ApiItemJSON | null | undefined>(undefined);
|
||||
|
||||
export const MemberProvider = ({
|
||||
member,
|
||||
children,
|
||||
}: {
|
||||
children?: ReactNode | undefined;
|
||||
member: ApiItemJSON | undefined;
|
||||
member: ApiItemJSON | null | undefined;
|
||||
}) => <MemberContext.Provider value={member}>{children}</MemberContext.Provider>;
|
||||
|
||||
export function useMember() {
|
||||
|
||||
19
apps/website/src/contexts/nav.tsx
Normal file
19
apps/website/src/contexts/nav.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
'use client';
|
||||
|
||||
import { type PropsWithChildren, type Dispatch, type SetStateAction, createContext, useContext, useState } from 'react';
|
||||
|
||||
export const NavContext = createContext<{ opened: boolean; setOpened: Dispatch<SetStateAction<boolean>> }>({
|
||||
opened: false,
|
||||
setOpened: (_) => {},
|
||||
});
|
||||
|
||||
export const NavProvider = ({ children }: PropsWithChildren) => {
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values
|
||||
return <NavContext.Provider value={{ opened, setOpened }}>{children}</NavContext.Provider>;
|
||||
};
|
||||
|
||||
export function useNav() {
|
||||
return useContext(NavContext);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ export default async function middleware(request: NextRequest) {
|
||||
}
|
||||
|
||||
if (PACKAGES.some((pkg) => request.nextUrl.pathname.includes(pkg))) {
|
||||
// eslint-disable-next-line prefer-named-capture-group
|
||||
const packageName = /\/docs\/packages\/([^/]+)\/.*/.exec(request.nextUrl.pathname)?.[1] ?? 'builders';
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`);
|
||||
const data: string[] = await res.json();
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import type { AppProps } from 'next/app';
|
||||
import Head from 'next/head';
|
||||
import NextProgress from 'next-progress';
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import '@unocss/reset/tailwind.css';
|
||||
import '../styles/unocss.css';
|
||||
import '../styles/cmdk.css';
|
||||
import '../styles/main.css';
|
||||
|
||||
export default function MyApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title key="title">discord.js</title>
|
||||
<meta content="minimum-scale=1, initial-scale=1, width=device-width" name="viewport" />
|
||||
<meta content="#5865f2" name="theme-color" />
|
||||
</Head>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
disableTransitionOnChange
|
||||
value={{
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
}}
|
||||
>
|
||||
<NextProgress color="#5865f2" options={{ showSpinner: false }} />
|
||||
<Component {...pageProps} />
|
||||
</ThemeProvider>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
import { DESCRIPTION } from '~/util/constants';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<link href="/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180" />
|
||||
<link href="/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png" />
|
||||
<link href="/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png" />
|
||||
<link href="/site.webmanifest" rel="manifest" />
|
||||
<link color="#090a16" href="/safari-pinned-tab.svg" rel="mask-icon" />
|
||||
<meta content="light dark" name="color-scheme" />
|
||||
<meta content="discord.js" name="apple-mobile-web-app-title" />
|
||||
<meta content="discord.js" name="application-name" />
|
||||
<meta content="#090a16" name="msapplication-TileColor" />
|
||||
<meta content={DESCRIPTION} key="description" name="description" />
|
||||
<meta content="discord.js" property="og:site_name" />
|
||||
<meta content="website" property="og:type" />
|
||||
<meta content="discord.js" key="og_title" property="og:title" />
|
||||
<meta content={DESCRIPTION} key="og_description" name="og:description" />
|
||||
<meta content="https://discordjs.dev/open-graph.png" property="og:image" />
|
||||
<meta content="summary_large_image" name="twitter:card" />
|
||||
<meta content="@iCrawlToGo" name="twitter:creator" />
|
||||
</Head>
|
||||
<body className="dark:bg-dark-800 bg-white">
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
44
apps/website/src/pages/api/og.tsx
Normal file
44
apps/website/src/pages/api/og.tsx
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable react/no-unknown-property */
|
||||
import { ImageResponse } from '@vercel/og';
|
||||
|
||||
const fonts = fetch(new URL('../../assets/fonts/Inter-Black.ttf', import.meta.url)).then(async (res) =>
|
||||
res.arrayBuffer(),
|
||||
);
|
||||
|
||||
export default async function handler() {
|
||||
const fontData = await fonts;
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
fontFamily: 'Inter',
|
||||
}}
|
||||
tw="flex flex-row bg-[#181818] h-full w-full"
|
||||
>
|
||||
<div tw="mx-auto flex flex-row items-center h-full">
|
||||
<div tw="flex flex-row">
|
||||
<div tw="flex flex-row">
|
||||
<div tw="flex flex-col font-black text-[5.5rem] text-white">
|
||||
<div tw="flex flex-row">
|
||||
The <span tw="bg-[#5865f2] rounded-lg py-1 px-6 ml-4">most popular</span>
|
||||
</div>
|
||||
<span>way to build Discord</span>
|
||||
<span>bots.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1_200,
|
||||
height: 630,
|
||||
fonts: [{ name: 'Inter', data: fontData, weight: 900, style: 'normal' }],
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
runtime: 'experimental-edge',
|
||||
};
|
||||
170
apps/website/src/pages/api/og_model.tsx
Normal file
170
apps/website/src/pages/api/og_model.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
/* eslint-disable react/no-unknown-property */
|
||||
import type { ApiItemKind } from '@microsoft/api-extractor-model';
|
||||
import { ImageResponse } from '@vercel/og';
|
||||
import type { NextRequest } from 'next/server';
|
||||
|
||||
const fonts = Promise.all([
|
||||
fetch(new URL('../../assets/fonts/Inter-Regular.ttf', import.meta.url)).then(async (res) => res.arrayBuffer()),
|
||||
fetch(new URL('../../assets/fonts/Inter-Bold.ttf', import.meta.url)).then(async (res) => res.arrayBuffer()),
|
||||
]);
|
||||
|
||||
function resolveIcon(icon: keyof typeof ApiItemKind, size = 88) {
|
||||
switch (icon) {
|
||||
case 'Class':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.34 9.71h.71l2.67-2.67v-.71L13.38 5h-.7l-1.82 1.81h-5V5.56l1.86-1.85V3l-2-2H5L1 5v.71l2 2h.71l1.14-1.15v5.79l.5.5H10v.52l1.33 1.34h.71l2.67-2.67v-.71L13.37 10h-.7l-1.86 1.85h-5v-4H10v.48l1.34 1.38zm1.69-3.65l.63.63-2 2-.63-.63 2-2zm0 5l.63.63-2 2-.63-.63 2-2zM3.35 6.65l-1.29-1.3 3.29-3.29 1.3 1.29-3.3 3.3z" />
|
||||
</svg>
|
||||
);
|
||||
case 'Enum':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M14 2H8L7 3v3h1V3h6v5h-4v1h4l1-1V3l-1-1zM9 6h4v1H9.41L9 6.59V6zM7 7H2L1 8v5l1 1h6l1-1V8L8 7H7zm1 6H2V8h6v5zM3 9h4v1H3V9zm0 2h4v1H3v-1zm6-7h4v1H9V4z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case 'EnumMember':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M7 3l1-1h6l1 1v5l-1 1h-4V8h4V3H8v3H7V3zm2 6V8L8 7H2L1 8v5l1 1h6l1-1V9zM8 8v5H2V8h6zm1.414-1L9 6.586V6h4v1H9.414zM9 4h4v1H9V4zm-2 6H3v1h4v-1z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case 'Interface':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.496 4a3.49 3.49 0 0 0-3.46 3h-3.1a2 2 0 1 0 0 1h3.1a3.5 3.5 0 1 0 3.46-4zm0 6a2.5 2.5 0 1 1 0-5 2.5 2.5 0 0 1 0 5z" />
|
||||
</svg>
|
||||
);
|
||||
case 'TypeAlias':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M14.45 4.5l-5-2.5h-.9l-7 3.5-.55.89v4.5l.55.9 5 2.5h.9l7-3.5.55-.9v-4.5l-.55-.89zm-8 8.64l-4.5-2.25V7.17l4.5 2v3.97zm.5-4.8L2.29 6.23l6.66-3.34 4.67 2.34-6.67 3.11zm7 1.55l-6.5 3.25V9.21l6.5-3v3.68z" />
|
||||
</svg>
|
||||
);
|
||||
case 'Variable':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M2 5h2V4H1.5l-.5.5v8l.5.5H4v-1H2V5zm12.5-1H12v1h2v7h-2v1h2.5l.5-.5v-8l-.5-.5zm-2.74 2.57L12 7v2.51l-.3.45-4.5 2h-.46l-2.5-1.5-.24-.43v-2.5l.3-.46 4.5-2h.46l2.5 1.5zM5 9.71l1.5.9V9.28L5 8.38v1.33zm.58-2.15l1.45.87 3.39-1.5-1.45-.87-3.39 1.5zm1.95 3.17l3.5-1.56v-1.4l-3.5 1.55v1.41z"
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
case 'Property':
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.807 14.975a1.75 1.75 0 0 1-1.255-.556 1.684 1.684 0 0 1-.544-1.1A1.72 1.72 0 0 1 1.36 12.1c1.208-1.27 3.587-3.65 5.318-5.345a4.257 4.257 0 0 1 .048-3.078 4.095 4.095 0 0 1 1.665-1.969 4.259 4.259 0 0 1 4.04-.36l.617.268-2.866 2.951 1.255 1.259 2.944-2.877.267.619a4.295 4.295 0 0 1 .04 3.311 4.198 4.198 0 0 1-.923 1.392 4.27 4.27 0 0 1-.743.581 4.217 4.217 0 0 1-3.812.446c-1.098 1.112-3.84 3.872-5.32 5.254a1.63 1.63 0 0 1-1.084.423zm7.938-13.047a3.32 3.32 0 0 0-1.849.557c-.213.13-.412.284-.591.458a3.321 3.321 0 0 0-.657 3.733l.135.297-.233.227c-1.738 1.697-4.269 4.22-5.485 5.504a.805.805 0 0 0 .132 1.05.911.911 0 0 0 .298.22c.1.044.209.069.319.072a.694.694 0 0 0 .45-.181c1.573-1.469 4.612-4.539 5.504-5.44l.23-.232.294.135a3.286 3.286 0 0 0 3.225-.254 3.33 3.33 0 0 0 .591-.464 3.28 3.28 0 0 0 .964-2.358c0-.215-.021-.43-.064-.642L11.43 7.125 8.879 4.578l2.515-2.59a3.286 3.286 0 0 0-.65-.06z" />
|
||||
</svg>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<svg fill="white" height={size} viewBox="0 0 16 16" width={size} xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.51 4l-5-3h-1l-5 3-.49.86v6l.49.85 5 3h1l5-3 .49-.85v-6L13.51 4zm-6 9.56l-4.5-2.7V5.7l4.5 2.45v5.41zM3.27 4.7l4.74-2.84 4.74 2.84-4.74 2.59L3.27 4.7zm9.74 6.16l-4.5 2.7V8.15l4.5-2.45v5.16z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default async function handler(req: NextRequest) {
|
||||
const fontData = await fonts;
|
||||
|
||||
const { searchParams } = new URL(req.url);
|
||||
|
||||
const hasPkg = searchParams.has('pkg');
|
||||
const hasKind = searchParams.has('kind');
|
||||
const hasName = searchParams.has('name');
|
||||
const hasMethods = searchParams.has('methods');
|
||||
const hasProps = searchParams.has('props');
|
||||
const hasMembers = searchParams.has('members');
|
||||
const pkg = hasPkg ? searchParams.get('pkg') : '';
|
||||
const kind = hasKind ? searchParams.get('kind')! : 'Method';
|
||||
const name = hasName ? searchParams.get('name')!.slice(0, 100) : 'My default name which is super long to overflow';
|
||||
const methods = hasMethods ? searchParams.get('methods') : '';
|
||||
const props = hasProps ? searchParams.get('props') : '';
|
||||
const members = hasMembers ? searchParams.get('members') : '';
|
||||
|
||||
return new ImageResponse(
|
||||
(
|
||||
<div
|
||||
style={{
|
||||
fontFamily: 'Inter',
|
||||
}}
|
||||
tw="flex flex-row bg-[#181818] h-full w-full p-24"
|
||||
>
|
||||
<div tw="flex flex-col mx-auto h-full text-white">
|
||||
<div tw="flex flex-row text-4xl text-gray-400">@discordjs/{pkg}</div>
|
||||
<div tw="flex flex-col justify-between h-full w-full pt-14">
|
||||
<div tw="flex flex-row items-center max-w-full">
|
||||
<span tw="mr-6">{resolveIcon(kind as keyof typeof ApiItemKind)}</span>
|
||||
<h2
|
||||
style={{
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
tw="text-[5.5rem] font-bold w-full"
|
||||
>
|
||||
{name}
|
||||
</h2>
|
||||
</div>
|
||||
<div tw="flex flex-row w-full justify-between">
|
||||
<div tw="flex flex-row">
|
||||
{props ? (
|
||||
<div tw="flex flex-row mr-12">
|
||||
<span tw="mr-4">{resolveIcon('Property', 36)}</span>
|
||||
<div tw="flex flex-col text-4xl">
|
||||
<span tw="mb-4">{props}</span>
|
||||
<span>Properties</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{methods ? (
|
||||
<div tw="flex flex-row mr-12">
|
||||
<span tw="mr-4">{resolveIcon('Method', 36)}</span>
|
||||
<div tw="flex flex-col text-4xl">
|
||||
<span tw="mb-4">{methods}</span>
|
||||
<span>Methods</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{members ? (
|
||||
<div tw="flex flex-row">
|
||||
<span tw="mr-4">{resolveIcon('EnumMember', 36)}</span>
|
||||
<div tw="flex flex-col text-4xl">
|
||||
<span tw="mb-4">{members}</span>
|
||||
<span>Members</span>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div tw="flex h-full items-end">
|
||||
<span tw="bg-[#5865f2] text-4xl font-black relative rounded-lg py-4 px-8">discord.js</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
{
|
||||
width: 1_200,
|
||||
height: 630,
|
||||
fonts: [
|
||||
{ name: 'Inter', data: fontData[0], weight: 500, style: 'normal' },
|
||||
{ name: 'Inter', data: fontData[1], weight: 700, style: 'normal' },
|
||||
],
|
||||
debug: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
runtime: 'experimental-edge',
|
||||
};
|
||||
@@ -1,287 +0,0 @@
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import process, { cwd } from 'node:process';
|
||||
import {
|
||||
findPackage,
|
||||
getMembers,
|
||||
type ApiItemJSON,
|
||||
type ApiClassJSON,
|
||||
type ApiFunctionJSON,
|
||||
type ApiInterfaceJSON,
|
||||
type ApiTypeAliasJSON,
|
||||
type ApiVariableJSON,
|
||||
type ApiEnumJSON,
|
||||
} from '@discordjs/api-extractor-utils';
|
||||
import { createApiModel } from '@discordjs/scripts';
|
||||
import { ApiFunction, ApiItemKind, type ApiPackage } from '@microsoft/api-extractor-model';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
import type { GetStaticPaths, GetStaticProps } from 'next/types';
|
||||
import { MDXRemote } from 'next-mdx-remote';
|
||||
import { serialize } from 'next-mdx-remote/serialize';
|
||||
import { useMemo } from 'react';
|
||||
import rehypeIgnore from 'rehype-ignore';
|
||||
import rehypePrettyCode, { type Options } from 'rehype-pretty-code';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { getHighlighter } from 'shiki';
|
||||
import shikiLangJavascript from 'shiki/languages/javascript.tmLanguage.json';
|
||||
import shikiLangTypescript from 'shiki/languages/typescript.tmLanguage.json';
|
||||
import shikiThemeDarkPlus from 'shiki/themes/dark-plus.json';
|
||||
import shikiThemeLightPlus from 'shiki/themes/light-plus.json';
|
||||
import { SidebarLayout, type SidebarLayoutProps } from '~/components/SidebarLayout';
|
||||
import { Class } from '~/components/model/Class';
|
||||
import { Enum } from '~/components/model/Enum';
|
||||
import { Function } from '~/components/model/Function';
|
||||
import { Interface } from '~/components/model/Interface';
|
||||
import { TypeAlias } from '~/components/model/TypeAlias';
|
||||
import { Variable } from '~/components/model/Variable';
|
||||
import { CmdKProvider } from '~/contexts/cmdK';
|
||||
import { MemberProvider } from '~/contexts/member';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
import { findMember, findMemberByKey } from '~/util/model.server';
|
||||
// import { miniSearch } from '~/util/search';
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const pkgs = (
|
||||
await Promise.all(
|
||||
PACKAGES.map(async (packageName) => {
|
||||
try {
|
||||
let data: any[] = [];
|
||||
let versions: string[] = [];
|
||||
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
|
||||
const res = await readFile(
|
||||
join(cwd(), '..', '..', 'packages', packageName, 'docs', 'docs.api.json'),
|
||||
'utf8',
|
||||
);
|
||||
data = JSON.parse(res);
|
||||
} else {
|
||||
const response = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`);
|
||||
versions = await response.json();
|
||||
versions = versions.slice(-2);
|
||||
|
||||
for (const version of versions) {
|
||||
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${version}.api.json`);
|
||||
data = [...data, await res.json()];
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(data)) {
|
||||
const models = data.map((innerData) => createApiModel(innerData));
|
||||
const pkgs = models.map((model) => findPackage(model, packageName)) as ApiPackage[];
|
||||
|
||||
return [
|
||||
...versions.map((version) => ({ params: { slug: ['packages', packageName, version] } })),
|
||||
...pkgs.flatMap((pkg, idx) =>
|
||||
getMembers(pkg, versions[idx]!).map((member) => {
|
||||
if (member.kind === ApiItemKind.Function && member.overloadIndex && member.overloadIndex > 1) {
|
||||
return {
|
||||
params: {
|
||||
slug: [
|
||||
'packages',
|
||||
packageName,
|
||||
versions[idx]!,
|
||||
`${member.name}:${member.overloadIndex}:${member.kind}`,
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
params: {
|
||||
slug: ['packages', packageName, versions[idx]!, `${member.name}:${member.kind}`],
|
||||
},
|
||||
};
|
||||
}),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
const model = createApiModel(data);
|
||||
const pkg = findPackage(model, packageName)!;
|
||||
|
||||
return [
|
||||
{ params: { slug: ['packages', packageName, 'main'] } },
|
||||
...getMembers(pkg, 'main').map((member) => {
|
||||
if (member.kind === ApiItemKind.Function && member.overloadIndex && member.overloadIndex > 1) {
|
||||
return {
|
||||
params: {
|
||||
slug: ['packages', packageName, 'main', `${member.name}:${member.overloadIndex}:${member.kind}`],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return { params: { slug: ['packages', packageName, 'main', `${member.name}:${member.kind}`] } };
|
||||
}),
|
||||
];
|
||||
} catch {
|
||||
return { params: { slug: [] } };
|
||||
}
|
||||
}),
|
||||
)
|
||||
).flat();
|
||||
|
||||
return {
|
||||
paths: pkgs,
|
||||
fallback: true,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const [path, packageName = 'builders', branchName = 'main', member] = params!.slug as string[];
|
||||
|
||||
if (path !== 'packages' || !PACKAGES.includes(packageName)) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
const [memberName, overloadIndex] = member?.split(':') ?? [];
|
||||
|
||||
try {
|
||||
const readme = await readFile(join(cwd(), '..', '..', 'packages', packageName, 'README.md'), 'utf8');
|
||||
|
||||
const mdxSource = await serialize(readme, {
|
||||
mdxOptions: {
|
||||
remarkPlugins: [remarkGfm],
|
||||
remarkRehypeOptions: { allowDangerousHtml: true },
|
||||
rehypePlugins: [
|
||||
rehypeRaw,
|
||||
rehypeIgnore,
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypePrettyCode,
|
||||
{
|
||||
theme: {
|
||||
dark: shikiThemeDarkPlus,
|
||||
light: shikiThemeLightPlus,
|
||||
},
|
||||
getHighlighter: async (options?: Partial<Options>) =>
|
||||
getHighlighter({
|
||||
...options,
|
||||
langs: [
|
||||
// @ts-expect-error: Working as intended
|
||||
{ id: 'javascript', aliases: ['js'], scopeName: 'source.js', grammar: shikiLangJavascript },
|
||||
// @ts-expect-error: Working as intended
|
||||
{ id: 'typescript', aliases: ['ts'], scopeName: 'source.ts', grammar: shikiLangTypescript },
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
],
|
||||
format: 'md',
|
||||
},
|
||||
});
|
||||
|
||||
let data;
|
||||
if (process.env.NEXT_PUBLIC_LOCAL_DEV) {
|
||||
const res = await readFile(join(cwd(), '..', '..', 'packages', packageName, 'docs', 'docs.api.json'), 'utf8');
|
||||
data = JSON.parse(res);
|
||||
} else {
|
||||
const res = await fetch(`https://docs.discordjs.dev/docs/${packageName}/${branchName}.api.json`);
|
||||
data = await res.json();
|
||||
}
|
||||
|
||||
const model = createApiModel(data);
|
||||
const pkg = findPackage(model, packageName);
|
||||
|
||||
// eslint-disable-next-line prefer-const
|
||||
let { containerKey, name } = findMember(model, packageName, memberName, branchName) ?? {};
|
||||
if (name && overloadIndex && !Number.isNaN(Number.parseInt(overloadIndex, 10))) {
|
||||
containerKey = ApiFunction.getContainerKey(name, Number.parseInt(overloadIndex, 10));
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
packageName,
|
||||
branchName,
|
||||
data: {
|
||||
members: pkg
|
||||
? getMembers(pkg, branchName).filter((item) => item.overloadIndex === null || item.overloadIndex <= 1)
|
||||
: [],
|
||||
member:
|
||||
memberName && containerKey ? findMemberByKey(model, packageName, containerKey, branchName) ?? null : null,
|
||||
source: mdxSource,
|
||||
},
|
||||
},
|
||||
revalidate: 3_600,
|
||||
};
|
||||
} catch (error_) {
|
||||
const error = error_ as Error;
|
||||
console.error(error);
|
||||
|
||||
return {
|
||||
props: {
|
||||
error: error.message,
|
||||
},
|
||||
revalidate: 1,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const member = (props?: ApiItemJSON | undefined) => {
|
||||
switch (props?.kind) {
|
||||
case 'Class':
|
||||
return <Class data={props as ApiClassJSON} />;
|
||||
case 'Function':
|
||||
return <Function data={props as ApiFunctionJSON} key={props.containerKey} />;
|
||||
case 'Interface':
|
||||
return <Interface data={props as ApiInterfaceJSON} />;
|
||||
case 'TypeAlias':
|
||||
return <TypeAlias data={props as ApiTypeAliasJSON} />;
|
||||
case 'Variable':
|
||||
return <Variable data={props as ApiVariableJSON} />;
|
||||
case 'Enum':
|
||||
return <Enum data={props as ApiEnumJSON} />;
|
||||
default:
|
||||
return <div>Cannot render that item type</div>;
|
||||
}
|
||||
};
|
||||
|
||||
export default function SlugPage(props: Partial<SidebarLayoutProps & { error?: string }>) {
|
||||
const router = useRouter();
|
||||
const name = useMemo(
|
||||
() => `discord.js${props.data?.member?.name ? ` | ${props.data.member.name}` : ''}`,
|
||||
[props.data?.member?.name],
|
||||
);
|
||||
const ogTitle = useMemo(
|
||||
() => `${props.packageName ?? 'discord.js'}${props.data?.member?.name ? ` | ${props.data.member.name}` : ''}`,
|
||||
[props.packageName, props.data?.member?.name],
|
||||
);
|
||||
|
||||
if (router.isFallback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Just in case
|
||||
// return <iframe src="https://discord.js.org" style={{ border: 0, height: '100%', width: '100%' }}></iframe>;
|
||||
|
||||
return props.error ? (
|
||||
<div className="flex h-full max-h-full w-full max-w-full flex-row">{props.error}</div>
|
||||
) : (
|
||||
<CmdKProvider>
|
||||
<MemberProvider member={props.data?.member}>
|
||||
<SidebarLayout {...props}>
|
||||
{props.data?.member ? (
|
||||
<>
|
||||
<Head>
|
||||
<title key="title">{name}</title>
|
||||
<meta content={ogTitle} key="og_title" property="og:title" />
|
||||
</Head>
|
||||
{member(props.data.member)}
|
||||
</>
|
||||
) : props.data?.source ? (
|
||||
<div className="prose max-w-none">
|
||||
<MDXRemote {...props.data.source} />
|
||||
</div>
|
||||
) : null}
|
||||
</SidebarLayout>
|
||||
</MemberProvider>
|
||||
</CmdKProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export const config = {
|
||||
unstable_includeFiles: [`../../packages/{${PACKAGES.join(',')}}/README.md`],
|
||||
};
|
||||
@@ -1,93 +0,0 @@
|
||||
import Link from 'next/link';
|
||||
import type { GetStaticPaths, GetStaticProps } from 'next/types';
|
||||
import { VscArrowLeft, VscArrowRight, VscVersions } from 'react-icons/vsc';
|
||||
import { PACKAGES } from '~/util/constants';
|
||||
|
||||
interface VersionProps {
|
||||
data: {
|
||||
versions: string[];
|
||||
};
|
||||
packageName: string;
|
||||
}
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = () => {
|
||||
const versions = PACKAGES.map((packageName) => ({ params: { package: packageName } }));
|
||||
|
||||
return {
|
||||
paths: versions,
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const getStaticProps: GetStaticProps = async ({ params }) => {
|
||||
const packageName = params!.package as string;
|
||||
|
||||
if (!PACKAGES.includes(packageName)) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`https://docs.discordjs.dev/api/info?package=${packageName}`);
|
||||
const data: string[] = await res.json();
|
||||
|
||||
if (!data.length) {
|
||||
return {
|
||||
notFound: true,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: {
|
||||
packageName,
|
||||
data: {
|
||||
versions: data.reverse(),
|
||||
},
|
||||
},
|
||||
revalidate: 3_600,
|
||||
};
|
||||
} catch (error_) {
|
||||
const error = error_ as Error;
|
||||
console.error(error);
|
||||
|
||||
return {
|
||||
props: {
|
||||
error: error.message,
|
||||
},
|
||||
revalidate: 1,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default function VersionsRoute(props: Partial<VersionProps> & { error?: string }) {
|
||||
return props.error ? (
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
{props.error}
|
||||
</div>
|
||||
) : (
|
||||
<div className="min-w-xs sm:w-md mx-auto flex h-full flex-row place-content-center place-items-center gap-8 py-0 px-4 lg:py-0 lg:px-6">
|
||||
<div className="flex grow flex-col place-content-center gap-4">
|
||||
<h1 className="text-2xl font-semibold">Select a version:</h1>
|
||||
{props.data?.versions.map((version) => (
|
||||
<Link href={`/docs/packages/${props.packageName}/${version}`} key={version} prefetch={false}>
|
||||
<a className="dark:bg-dark-400 dark:border-dark-100 dark:hover:bg-dark-300 dark:active:bg-dark-200 focus:ring-width-2 focus:ring-blurple flex flex h-11 transform-gpu cursor-pointer select-none appearance-none flex-col place-content-center rounded border border-neutral-300 bg-transparent p-4 text-base font-semibold leading-none text-black outline-0 hover:bg-neutral-100 focus:ring active:translate-y-px active:bg-neutral-200 dark:text-white">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<div className="flex flex-row place-content-between place-items-center gap-4">
|
||||
<VscVersions size={25} />
|
||||
<h2 className="font-semibold">{version}</h2>
|
||||
</div>
|
||||
<VscArrowRight size={20} />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)) ?? null}
|
||||
<Link href="/docs/packages" prefetch={false}>
|
||||
<a className="bg-blurple focus:ring-width-2 flex h-11 transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 place-self-center rounded border-0 px-4 text-base font-semibold leading-none text-white no-underline outline-0 focus:ring focus:ring-white active:translate-y-px">
|
||||
<VscArrowLeft size={20} /> Go back
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user