Compare commits
432 Commits
@discordjs
...
@discordjs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca81e61b4c | ||
|
|
eb603c0d11 | ||
|
|
072c9da5df | ||
|
|
cd591327d6 | ||
|
|
71e67d8c97 | ||
|
|
a9e0de4288 | ||
|
|
c5b4423a5a | ||
|
|
47b08be509 | ||
|
|
2e0804ae92 | ||
|
|
71983cc5d1 | ||
|
|
0f3d870ce6 | ||
|
|
ce4e1fc2c6 | ||
|
|
0762ddb3f4 | ||
|
|
80af9696d3 | ||
|
|
48cab844d4 | ||
|
|
8b71f44b1f | ||
|
|
f27631175a | ||
|
|
ad217cc760 | ||
|
|
1ba7394a7d | ||
|
|
260c46d51c | ||
|
|
6229597db2 | ||
|
|
8d212e4859 | ||
|
|
51e8f02ef7 | ||
|
|
496c4e2884 | ||
|
|
77191a2e7b | ||
|
|
375483b0f9 | ||
|
|
17cd4b610a | ||
|
|
988f8c7546 | ||
|
|
217e5d8100 | ||
|
|
40d07fbbbd | ||
|
|
1b15d31b5a | ||
|
|
d26df5faba | ||
|
|
78fe247fc4 | ||
|
|
36216c0e1a | ||
|
|
6212bffa30 | ||
|
|
b2e1bb92d0 | ||
|
|
2e09cb4c1f | ||
|
|
1b4d34cebf | ||
|
|
8153ca99ee | ||
|
|
43e205b840 | ||
|
|
c6ca5a83e7 | ||
|
|
5c52bb9590 | ||
|
|
a49ed0a2d5 | ||
|
|
3e01f91bbb | ||
|
|
7d341000d4 | ||
|
|
b49c5c9f48 | ||
|
|
ce287f21d1 | ||
|
|
d6bca9bb4d | ||
|
|
a2ab2b8717 | ||
|
|
ecd1b5da11 | ||
|
|
0eb866357b | ||
|
|
d2d27ce734 | ||
|
|
603446a692 | ||
|
|
f1fdd5b010 | ||
|
|
5d1a4c27d5 | ||
|
|
98a76db482 | ||
|
|
516c46117c | ||
|
|
7d7972d239 | ||
|
|
7b10e8cb63 | ||
|
|
2f18323d2c | ||
|
|
e04801bba1 | ||
|
|
09a9fb57e0 | ||
|
|
025b7aea29 | ||
|
|
7aa5ea731f | ||
|
|
f883279ab3 | ||
|
|
f1b2dec8e1 | ||
|
|
0a1701b046 | ||
|
|
5dcc6df224 | ||
|
|
1c35425120 | ||
|
|
cf1f5c0f0c | ||
|
|
a210fc64a2 | ||
|
|
24f17998c8 | ||
|
|
eda118dae9 | ||
|
|
f2fce0a7da | ||
|
|
29389e39f4 | ||
|
|
d5e9e36e8a | ||
|
|
8c0eb56ba5 | ||
|
|
1b74c774c0 | ||
|
|
01949d6e3a | ||
|
|
3ea26cc1a6 | ||
|
|
a516d46f91 | ||
|
|
b35b935679 | ||
|
|
0928c8f6ff | ||
|
|
188d5eea28 | ||
|
|
412e4fffae | ||
|
|
5e2f94c591 | ||
|
|
49627b1f46 | ||
|
|
bcb48fea3e | ||
|
|
c87e826087 | ||
|
|
2dddbe1f32 | ||
|
|
86e5f5a119 | ||
|
|
300059cb26 | ||
|
|
d4c1fecbe2 | ||
|
|
79bcdfa767 | ||
|
|
ca4de2d9c6 | ||
|
|
794abe8450 | ||
|
|
02dfaf1aa2 | ||
|
|
cac3c07729 | ||
|
|
83143178aa | ||
|
|
615752e32b | ||
|
|
2c25639a85 | ||
|
|
a76c1ddacc | ||
|
|
0d0e4d1cb6 | ||
|
|
d35f670d7a | ||
|
|
e4c5f794b0 | ||
|
|
88cab1a0ec | ||
|
|
1c2cc09e05 | ||
|
|
2a684361d4 | ||
|
|
66dc4014fe | ||
|
|
39c6694561 | ||
|
|
70da3746f8 | ||
|
|
0340622f1a | ||
|
|
f36878677c | ||
|
|
e5859b41cf | ||
|
|
8218ffc78d | ||
|
|
7ff3d528d9 | ||
|
|
0b7f296b62 | ||
|
|
85a379fdf8 | ||
|
|
8ffcf77840 | ||
|
|
fcb3a1ab27 | ||
|
|
40be4f80dc | ||
|
|
ee907f32f3 | ||
|
|
731ea5f3cb | ||
|
|
24c462bf6f | ||
|
|
78e02c4b63 | ||
|
|
dcf8757d35 | ||
|
|
a0c57abadd | ||
|
|
1c5a6fa552 | ||
|
|
6a221a9676 | ||
|
|
dcf58d8140 | ||
|
|
676307ff5c | ||
|
|
23e0ac56f4 | ||
|
|
8073561824 | ||
|
|
c0f2dd7131 | ||
|
|
d6905b3b97 | ||
|
|
774e23c572 | ||
|
|
1c8567f147 | ||
|
|
733c96c255 | ||
|
|
69cdeb7296 | ||
|
|
0019700869 | ||
|
|
3ea4d26ee9 | ||
|
|
250eccf118 | ||
|
|
7d3827ebd1 | ||
|
|
d1955f7c9e | ||
|
|
6412da4921 | ||
|
|
79123fb260 | ||
|
|
3d2f4b405e | ||
|
|
bfee6c8d88 | ||
|
|
fbbce3eb4b | ||
|
|
b2eec5f9fc | ||
|
|
b618e215f2 | ||
|
|
d66d113333 | ||
|
|
1b9d07f941 | ||
|
|
f893c6a357 | ||
|
|
687bbd889f | ||
|
|
f195556b64 | ||
|
|
9aafdd6214 | ||
|
|
e3e7d500dc | ||
|
|
3615e2f2d2 | ||
|
|
a7425c29c4 | ||
|
|
c519fe66c9 | ||
|
|
24f280290b | ||
|
|
6912faa9b3 | ||
|
|
0645bf0f7f | ||
|
|
6aba9e99eb | ||
|
|
5efdf57894 | ||
|
|
74059c9d97 | ||
|
|
8d345a041e | ||
|
|
1cbd04e8fd | ||
|
|
8592edba39 | ||
|
|
38f0986000 | ||
|
|
9e077b749c | ||
|
|
b977ba5f4d | ||
|
|
1a6db13f6f | ||
|
|
3334739f4f | ||
|
|
26b2f59bdc | ||
|
|
b0ecc57977 | ||
|
|
035203f0d9 | ||
|
|
ce7d6b47b7 | ||
|
|
11e682cfe3 | ||
|
|
9d69bba47c | ||
|
|
2792e48119 | ||
|
|
384b4d10e8 | ||
|
|
d16114c526 | ||
|
|
de1aac674a | ||
|
|
79875658cf | ||
|
|
ab3328a0e2 | ||
|
|
1864d37d36 | ||
|
|
f340f3b1fd | ||
|
|
907eb1b470 | ||
|
|
e2f39ccc32 | ||
|
|
74a6d59ae9 | ||
|
|
a5b0d97224 | ||
|
|
52c6ea2fdb | ||
|
|
cfa48cedc3 | ||
|
|
cf23149d17 | ||
|
|
311cab2d3f | ||
|
|
34bc36ac4b | ||
|
|
db8df104c5 | ||
|
|
984bd55b43 | ||
|
|
3cf4f4b317 | ||
|
|
fd008f0144 | ||
|
|
8deef3e93f | ||
|
|
89235f32b0 | ||
|
|
47da24ff5c | ||
|
|
d1ebe4a52c | ||
|
|
5cdd5d76ed | ||
|
|
b8b852ee1e | ||
|
|
ba93bc8141 | ||
|
|
fbd599d586 | ||
|
|
d9a9500b40 | ||
|
|
51de9668e5 | ||
|
|
b097b25116 | ||
|
|
1d2c152320 | ||
|
|
d8d5f31d39 | ||
|
|
abd6ae9fc8 | ||
|
|
44ef2d9485 | ||
|
|
519825a651 | ||
|
|
ad31edc7aa | ||
|
|
d6f4e60efd | ||
|
|
5fcd0e48a8 | ||
|
|
52f8e0670c | ||
|
|
1c4af93898 | ||
|
|
3bd76078e1 | ||
|
|
71eba0e1b2 | ||
|
|
d284b8c64b | ||
|
|
795e6c363d | ||
|
|
d6cd3fd7ed | ||
|
|
d144a78813 | ||
|
|
645e2d3d6b | ||
|
|
5b745a49d8 | ||
|
|
ee5169e0aa | ||
|
|
bc641fa936 | ||
|
|
c8c02f957d | ||
|
|
091824abc5 | ||
|
|
03f5f1e3b6 | ||
|
|
852fae557e | ||
|
|
5f93dcce46 | ||
|
|
22e880aaa0 | ||
|
|
e9a8eb323f | ||
|
|
cc57563e73 | ||
|
|
519e163f8a | ||
|
|
eb81dc982c | ||
|
|
bf507ab265 | ||
|
|
178c8dcfee | ||
|
|
56cf138e02 | ||
|
|
950fc47234 | ||
|
|
c76b17d3b3 | ||
|
|
229ad077ff | ||
|
|
2e8e95031c | ||
|
|
98420826bc | ||
|
|
51edba78bc | ||
|
|
41077c96b5 | ||
|
|
869153c3fd | ||
|
|
955e8fe312 | ||
|
|
4458a13925 | ||
|
|
334a51240a | ||
|
|
dff131e8e4 | ||
|
|
f5ec1cada5 | ||
|
|
a99fc64e3f | ||
|
|
6ecff26ec6 | ||
|
|
646b6ead7e | ||
|
|
3492b194b5 | ||
|
|
1b29099ed0 | ||
|
|
c6d98fa0c5 | ||
|
|
f65ac2ea78 | ||
|
|
a63ac88fcc | ||
|
|
4367ab9302 | ||
|
|
dc142c47e4 | ||
|
|
1f86d8578a | ||
|
|
71a427f632 | ||
|
|
664cccb270 | ||
|
|
27e0b32c5f | ||
|
|
ffdb197f98 | ||
|
|
095bd77515 | ||
|
|
e78b8ad3fb | ||
|
|
c1000b86ed | ||
|
|
a9495bd8f0 | ||
|
|
4e0e125039 | ||
|
|
6e481f0a30 | ||
|
|
fd0246ca4c | ||
|
|
9681f34877 | ||
|
|
7f2ef962ec | ||
|
|
5c5a5832b9 | ||
|
|
c6f9c50ba9 | ||
|
|
765d5a3b2d | ||
|
|
dc9924fb5f | ||
|
|
da23cd5d69 | ||
|
|
76b21162ac | ||
|
|
4e0a89f58f | ||
|
|
483cbb3b2a | ||
|
|
9156a2889c | ||
|
|
9439107a1d | ||
|
|
1ba1f238f0 | ||
|
|
6f78e8285b | ||
|
|
f268e1d979 | ||
|
|
1ba22f4c9e | ||
|
|
0b76ab4c40 | ||
|
|
f70df910ed | ||
|
|
b8684e0585 | ||
|
|
071516c352 | ||
|
|
d44a6f035c | ||
|
|
0e4224be36 | ||
|
|
405f9400e8 | ||
|
|
8b70f497a1 | ||
|
|
56ef3ad8a0 | ||
|
|
2e48faf91a | ||
|
|
13ce78af6e | ||
|
|
b803a9a899 | ||
|
|
6a9875da05 | ||
|
|
8c265b628d | ||
|
|
1831acaffb | ||
|
|
05a1cbfe54 | ||
|
|
1c871b5b57 | ||
|
|
bb58395df0 | ||
|
|
f6506e99c4 | ||
|
|
a580768cda | ||
|
|
cb43db2180 | ||
|
|
368b8ba73c | ||
|
|
d28af8eca7 | ||
|
|
4224d783bc | ||
|
|
3659e0de67 | ||
|
|
38e115b908 | ||
|
|
4cedc21dbb | ||
|
|
8d327aa416 | ||
|
|
0a0c7d32d6 | ||
|
|
a4f2f1b5a5 | ||
|
|
859111f864 | ||
|
|
4accb00b98 | ||
|
|
41132b1049 | ||
|
|
e042492486 | ||
|
|
2798ba1eb3 | ||
|
|
8dfd0037b5 | ||
|
|
828a13b526 | ||
|
|
39c4de2dbc | ||
|
|
158db474b7 | ||
|
|
a995cbc0fa | ||
|
|
7ab62b4bb7 | ||
|
|
7dad50ee9a | ||
|
|
f3fe3ced62 | ||
|
|
ecb4281d1e | ||
|
|
0ff67d8e7a | ||
|
|
e8b7504352 | ||
|
|
0076589ccc | ||
|
|
816aed478e | ||
|
|
3407e1eea3 | ||
|
|
7dec892218 | ||
|
|
6d7a143667 | ||
|
|
be294eaf99 | ||
|
|
876b181312 | ||
|
|
d3e9f2a355 | ||
|
|
ad49845260 | ||
|
|
d7a09f6fce | ||
|
|
2e22b31892 | ||
|
|
f89a164bac | ||
|
|
4ee00b6534 | ||
|
|
d8dd197a93 | ||
|
|
898b5ac416 | ||
|
|
3160ab0fc7 | ||
|
|
a6941d536c | ||
|
|
bec51de103 | ||
|
|
5915f39810 | ||
|
|
5dc5e90268 | ||
|
|
07b597df16 | ||
|
|
36560c9955 | ||
|
|
43ce2a572e | ||
|
|
22e2bbb0d2 | ||
|
|
7ce990954e | ||
|
|
0b8b114761 | ||
|
|
6614603326 | ||
|
|
153352ad7a | ||
|
|
429dbccc85 | ||
|
|
25c27eac14 | ||
|
|
5eab5fc06c | ||
|
|
0cf29abcf0 | ||
|
|
dd62be077d | ||
|
|
83eafb6146 | ||
|
|
18b3a19810 | ||
|
|
c76e170786 | ||
|
|
cbafd479b3 | ||
|
|
19519ec9fc | ||
|
|
166f742d02 | ||
|
|
7a61004627 | ||
|
|
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 |
@@ -7,8 +7,5 @@
|
||||
"rules": {
|
||||
"@typescript-eslint/consistent-type-definitions": ["error", "interface"]
|
||||
},
|
||||
"ignorePatterns": ["**/dist/*"],
|
||||
"env": {
|
||||
"jest": true
|
||||
}
|
||||
"ignorePatterns": ["**/dist/*"]
|
||||
}
|
||||
|
||||
27
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
# Learn how to add code owners here:
|
||||
# https://help.github.com/en/articles/about-code-owners
|
||||
|
||||
* @iCrawl
|
||||
|
||||
/apps/guide/ @discordjs/website @discordjs/guide
|
||||
/apps/guide/src/content/ @discordjs/guide
|
||||
/apps/website/ @discordjs/website
|
||||
|
||||
/packages/actions/ @discordjs/actions
|
||||
/packages/api-extractor-utils/ @discordjs/api-extractor-utils
|
||||
/packages/brokers/ @discordjs/brokers
|
||||
/packages/builders/ @discordjs/builders
|
||||
/packages/collection/ @discordjs/collection
|
||||
/packages/core/ @discordjs/core
|
||||
/packages/discord.js/ @discordjs/core
|
||||
/packages/docgen/ @iCrawl
|
||||
/packages/formatters/ @discordjs/formatters
|
||||
/packages/next/ @discordjs/core
|
||||
/packages/proxy/ @discordjs/proxy
|
||||
/packages/proxy-container/ @discordjs/proxy
|
||||
/packages/rest/ @discordjs/rest
|
||||
/packages/scripts/ @discordjs/scripts
|
||||
/packages/ui/ @discordjs/ui
|
||||
/packages/util/ @discordjs/util
|
||||
/packages/voice/ @discordjs/core
|
||||
/packages/ws/ @discordjs/ws
|
||||
2
.github/CONTRIBUTING.md
vendored
@@ -40,7 +40,7 @@ For example, to automatically recompile the `@discordjs/rest` project when chang
|
||||
|
||||
If you'd like to create another package under the `@discordjs` organization run the following command:
|
||||
|
||||
```bash
|
||||
```sh
|
||||
yarn create-package <package-name> [package-description]
|
||||
```
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
name: Bug report
|
||||
description: Report incorrect or unexpected behavior of a package
|
||||
description: Report an issue with discord.js or another package.
|
||||
labels: [bug, need repro]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Use Discord for questions: https://discord.gg/djs
|
||||
Thank you for filing an issue! If you are here to ask a question, use Discord instead: https://discord.gg/djs
|
||||
|
||||
This issue form is for discord.js, including other packages.
|
||||
- type: dropdown
|
||||
id: package
|
||||
attributes:
|
||||
@@ -16,9 +18,11 @@ body:
|
||||
- builders
|
||||
- collection
|
||||
- core
|
||||
- rest
|
||||
- formatters
|
||||
- next
|
||||
- proxy
|
||||
- proxy-container
|
||||
- rest
|
||||
- voice
|
||||
- ws
|
||||
validations:
|
||||
@@ -27,57 +31,44 @@ body:
|
||||
id: description
|
||||
attributes:
|
||||
label: Issue description
|
||||
description: |
|
||||
Describe the issue in as much detail as possible.
|
||||
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files into it.
|
||||
description: Describe the issue in as much detail as possible.
|
||||
placeholder: |
|
||||
Steps to reproduce with below code sample:
|
||||
1. do thing
|
||||
2. do thing in Discord client
|
||||
3. observe behavior
|
||||
4. see error logs below
|
||||
1. Do thing
|
||||
2. Do thing in Discord client
|
||||
3. Observe behavior
|
||||
4. See error logs below
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: codesample
|
||||
id: code_sample
|
||||
attributes:
|
||||
label: Code sample
|
||||
description: Include a reproducible, minimal code sample. This will be automatically formatted into code, so no need for backticks.
|
||||
render: typescript
|
||||
placeholder: |
|
||||
Your code sample should be...
|
||||
... Minimal - Use as little code as possible that still produces the same problem (and is understandable)
|
||||
... Complete - Provide all parts someone else needs to reproduce your problem
|
||||
... Reproducible - Test the code you're about to provide to make sure it reproduces the problem
|
||||
- type: input
|
||||
id: djs-version
|
||||
attributes:
|
||||
label: Package version
|
||||
description: Which version of the package are you using? Run `npm list <package>` in your project directory and paste the output.
|
||||
placeholder: We no longer support version 12 or earlier of discord.js
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: node-version
|
||||
attributes:
|
||||
label: Node.js version
|
||||
description: |
|
||||
Which version of Node.js are you using? Run `node --version` in your project directory and paste the output.
|
||||
If you are using TypeScript, please include its version (`npm list typescript`) as well.
|
||||
placeholder: Node.js version 16.9+ is required for version 14.0.0+
|
||||
Your code sample should be:
|
||||
1. Minimal - Use as little code as possible that still produces the same problem (and is understandable)
|
||||
2. Complete - Provide all parts someone else needs to reproduce your problem
|
||||
3. Reproducible - Test the code you're about to provide to make sure it reproduces the problem
|
||||
|
||||
This will be automatically formatted into code, so no need for backticks.
|
||||
render: typescript
|
||||
- type: textarea
|
||||
id: versions
|
||||
attributes:
|
||||
label: Versions
|
||||
description: List necessary versions here. This includes your package version, runtime version, operating system etc.
|
||||
placeholder: |
|
||||
- discord.js 14.9.0 (`npm ls discord.js` or another package)
|
||||
- Node.js 16.9.0 (`node --version`)
|
||||
- TypeScript 5.0.4 (`npm ls typescript` if you use it)
|
||||
- macOS Ventura 13.3.1
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating system
|
||||
description: Which OS does your application run on?
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority this issue should have
|
||||
description: Please be realistic. If you need to elaborate on your reasoning, please use the Issue description field above.
|
||||
label: Issue priority
|
||||
description: Please be realistic. If you need to elaborate on your reasoning, please use the issue description field above.
|
||||
options:
|
||||
- Low (slightly annoying)
|
||||
- Medium (should be fixed soon)
|
||||
@@ -88,12 +79,9 @@ body:
|
||||
id: partials
|
||||
attributes:
|
||||
label: Which partials do you have configured?
|
||||
description: |
|
||||
Check your Client constructor for the `partials` key.
|
||||
|
||||
Tip: you can select multiple items
|
||||
description: Check your `Client` constructor for the `partials` key.
|
||||
options:
|
||||
- Not applicable (subpackage bug)
|
||||
- Not applicable
|
||||
- No Partials
|
||||
- User
|
||||
- Channel
|
||||
@@ -109,16 +97,13 @@ body:
|
||||
id: intents
|
||||
attributes:
|
||||
label: Which gateway intents are you subscribing to?
|
||||
description: |
|
||||
Check your Client constructor options for the `intents` key.
|
||||
|
||||
Tip: you can select multiple items
|
||||
description: Check your `Client` constructor for the `intents` key.
|
||||
options:
|
||||
- Not applicable (subpackage bug)
|
||||
- Not applicable
|
||||
- No Intents
|
||||
- Guilds
|
||||
- GuildMembers
|
||||
- GuildBans
|
||||
- GuildModeration
|
||||
- GuildEmojisAndStickers
|
||||
- GuildIntegrations
|
||||
- GuildWebhooks
|
||||
@@ -139,7 +124,7 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: dev-release
|
||||
id: dev_release
|
||||
attributes:
|
||||
label: I have tested this issue on a development release
|
||||
placeholder: d23280c (commit hash)
|
||||
58
.github/ISSUE_TEMPLATE/02-application_bug_report.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Websites bug report
|
||||
description: Report an issue with the documentation or guide websites.
|
||||
labels: [bug, need repro]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for filing an issue! If you are here to ask a question, use Discord instead: https://discord.gg/djs
|
||||
|
||||
This issue form is for our documentation and guide websites.
|
||||
- type: dropdown
|
||||
id: application
|
||||
attributes:
|
||||
label: Which application is this bug report for?
|
||||
options:
|
||||
- Documentation
|
||||
- Guide
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Issue description
|
||||
description: Describe the issue in as much detail as possible.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps_to_reproduce
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: What steps must be taken to reproduce this issue?
|
||||
placeholder: |
|
||||
1. Visit a page
|
||||
2. Click a link
|
||||
3. ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: versions
|
||||
attributes:
|
||||
label: Versions
|
||||
description: List necessary versions here. This includes your browser, operating system etc.
|
||||
placeholder: |
|
||||
- Safari 16.4 (18615.1.26.11.23)
|
||||
- macOS Ventura 13.3.1
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Issue priority
|
||||
description: Please be realistic. If you need to elaborate on your reasoning, please use the issue description field above.
|
||||
options:
|
||||
- Low (slightly annoying)
|
||||
- Medium (should be fixed soon)
|
||||
- High (immediate attention needed)
|
||||
validations:
|
||||
required: true
|
||||
@@ -9,17 +9,22 @@ body:
|
||||
We do not implement unreleased features.
|
||||
Use Discord for questions: https://discord.gg/djs
|
||||
- type: dropdown
|
||||
id: package
|
||||
id: application_or_package
|
||||
attributes:
|
||||
label: Which package is the feature request for?
|
||||
label: Which application or package is this feature request for?
|
||||
options:
|
||||
- discord.js
|
||||
- Documentation
|
||||
- Guide
|
||||
- brokers
|
||||
- builders
|
||||
- collection
|
||||
- core
|
||||
- rest
|
||||
- formatters
|
||||
- next
|
||||
- proxy
|
||||
- proxy-container
|
||||
- rest
|
||||
- voice
|
||||
- ws
|
||||
validations:
|
||||
@@ -45,7 +50,7 @@ body:
|
||||
label: Alternative solutions or implementations
|
||||
description: A clear and concise description of any alternative solutions or features you have considered.
|
||||
- type: textarea
|
||||
id: additional-context
|
||||
id: additional_context
|
||||
attributes:
|
||||
label: Other context
|
||||
description: Any other context, screenshots, or file uploads that help us understand your feature request.
|
||||
8
.github/auto_assign.yml
vendored
@@ -1,8 +0,0 @@
|
||||
addReviewers: true
|
||||
reviewers:
|
||||
- iCrawl
|
||||
- SpaceEEC
|
||||
- kyranet
|
||||
- vladfrangu
|
||||
numberOfReviewers: 0
|
||||
runOnDraft: true
|
||||
42
.github/issue-labeler.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
apps:guide:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nGuide\\n"
|
||||
apps:website:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nDocumentation\\n"
|
||||
packages:brokers:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nbrokers\\n"
|
||||
packages:builders:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nbuilders\\n"
|
||||
packages:collection:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\ncollection\\n"
|
||||
packages:core:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\ncore\\n"
|
||||
packages:discord.js:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\ndiscord.js\\n"
|
||||
packages:formatters:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nformatters\\n"
|
||||
packages:next:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nnext\\n"
|
||||
packages:proxy:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nproxy\\n"
|
||||
packages:proxy-container:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nproxy-container\\n"
|
||||
packages:rest:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nrest\\n"
|
||||
packages:voice:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nvoice\\n"
|
||||
packages:ws:
|
||||
- "### Which (application|package|application or package) is this (bug
|
||||
report|feature request) for\\?\\n\\nws\\n"
|
||||
13
.github/labeler.yml
vendored
@@ -4,7 +4,6 @@ apps:guide:
|
||||
apps:website:
|
||||
- apps/website/*
|
||||
- apps/website/**/*
|
||||
|
||||
packages:brokers:
|
||||
- packages/brokers/*
|
||||
- packages/brokers/**/*
|
||||
@@ -20,9 +19,15 @@ packages:core:
|
||||
packages:discord.js:
|
||||
- packages/discord.js/*
|
||||
- packages/discord.js/**/*
|
||||
packages:next:
|
||||
- packages/next/*
|
||||
- packages/next/**/*
|
||||
packages:docgen:
|
||||
- packages/docgen/*
|
||||
- packages/docgen/**/*
|
||||
packages:formatters:
|
||||
- packages/formatters/*
|
||||
- packages/formatters/**/*
|
||||
packages:proxy:
|
||||
- packages/proxy/*
|
||||
- packages/proxy/**/*
|
||||
@@ -32,9 +37,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
@@ -8,6 +8,8 @@
|
||||
color: fbca04
|
||||
- name: backlog
|
||||
color: 7ef7ef
|
||||
- name: backport
|
||||
color: 88aabb
|
||||
- name: blocked
|
||||
color: fc1423
|
||||
- name: bug
|
||||
@@ -20,10 +22,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,12 +42,12 @@
|
||||
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
|
||||
@@ -60,8 +62,12 @@
|
||||
color: fbca04
|
||||
- name: packages:discord.js
|
||||
color: fbca04
|
||||
- name: packages:next
|
||||
color: fbca04
|
||||
- name: packages:docgen
|
||||
color: fbca04
|
||||
- name: packages:formatters
|
||||
color: fbca04
|
||||
- name: packages:proxy
|
||||
color: fbca04
|
||||
- name: packages:proxy-container
|
||||
|
||||
108
.github/workflows/documentation.yml
vendored
@@ -3,6 +3,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- 'packages/*/src/**'
|
||||
tags:
|
||||
- '**'
|
||||
workflow_dispatch:
|
||||
@@ -22,7 +24,7 @@ concurrency:
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
build:
|
||||
name: Build documentation
|
||||
name: Build & upload documentation
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
@@ -34,10 +36,10 @@ jobs:
|
||||
with:
|
||||
ref: ${{ inputs.ref || '' }}
|
||||
|
||||
- name: Install node.js v16
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
@@ -48,57 +50,6 @@ jobs:
|
||||
- name: Build docs
|
||||
run: yarn docs
|
||||
|
||||
- name: Upload docgen artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: docgen
|
||||
path: packages/*/docs/docs.json
|
||||
|
||||
- name: Upload api-extractor artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: api-extractor
|
||||
path: packages/*/docs/docs.api.json
|
||||
|
||||
upload:
|
||||
name: Upload Documentation
|
||||
needs: build
|
||||
strategy:
|
||||
max-parallel: 1
|
||||
fail-fast: false
|
||||
matrix:
|
||||
package: ['brokers', 'builders', 'collection', 'core', 'discord.js', 'proxy', 'rest', 'util', 'voice', 'ws']
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v16
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build actions
|
||||
run: yarn workspace @discordjs/actions build
|
||||
|
||||
- name: Download docgen artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: docgen
|
||||
path: docs
|
||||
|
||||
- name: Download api-extractor artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: api-extractor
|
||||
path: docs
|
||||
|
||||
- name: Checkout docs repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
@@ -111,34 +62,49 @@ jobs:
|
||||
id: extract-tag
|
||||
uses: ./packages/actions/src/formatTag
|
||||
with:
|
||||
tag: ${{ env.GITHUB_REF_NAME }}
|
||||
tag: ${{ github.ref_name }}
|
||||
|
||||
- name: Upload documentation to database
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
uses: ./packages/actions/src/uploadDocumentation
|
||||
with:
|
||||
package: ${{ steps.extract-tag.outputs.package }}
|
||||
version: ${{ steps.extract-tag.outputs.semver }}
|
||||
|
||||
- name: Move docs to correct directory
|
||||
if: ${{ github.ref_type == 'tag' && matrix.package == steps.extract-tag.outputs.package }}
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
env:
|
||||
PACKAGE: ${{ steps.extract-tag.outputs.package }}
|
||||
SEMVER: ${{ steps.extract-tag.outputs.semver }}
|
||||
run: |
|
||||
mkdir -p out/${PACKAGE}
|
||||
if [[ $PACKAGE == "discord.js" ]]; then
|
||||
mv docs/${PACKAGE}/docs/docs.json out/${PACKAGE}/${SEMVER}.json
|
||||
fi
|
||||
if [[ $PACKAGE != "discord.js" ]]; then
|
||||
mv docs/${PACKAGE}/docs/docs.api.json out/${PACKAGE}/${SEMVER}.api.json
|
||||
mkdir -p "out/${PACKAGE}"
|
||||
if [[ "${PACKAGE}" == "discord.js" ]]; then
|
||||
mv "packages/${PACKAGE}/docs/docs.json" "out/${PACKAGE}/${SEMVER}.json"
|
||||
else
|
||||
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${SEMVER}.api.json"
|
||||
fi
|
||||
|
||||
- name: Upload documentation to database
|
||||
if: ${{ github.ref_type == 'branch' }}
|
||||
env:
|
||||
DATABASE_URL: ${{ secrets.DATABASE_URL }}
|
||||
uses: ./packages/actions/src/uploadDocumentation
|
||||
|
||||
- name: Move docs to correct directory
|
||||
if: ${{ github.ref_type == 'branch' }}
|
||||
env:
|
||||
PACKAGE: ${{ matrix.package }}
|
||||
run: |
|
||||
mkdir -p out/${PACKAGE}
|
||||
if [[ $PACKAGE == "discord.js" ]]; then
|
||||
mv docs/${PACKAGE}/docs/docs.json out/${PACKAGE}/${GITHUB_REF_NAME}.json
|
||||
fi
|
||||
if [[ $PACKAGE != "discord.js" ]]; then
|
||||
mv docs/${PACKAGE}/docs/docs.api.json out/${PACKAGE}/${GITHUB_REF_NAME}.api.json
|
||||
fi
|
||||
declare -a PACKAGES=("brokers" "builders" "collection" "core" "discord.js" "next" "formatters" "proxy" "rest" "util" "voice" "ws")
|
||||
for PACKAGE in "${PACKAGES[@]}"; do
|
||||
if [[ "${PACKAGE}" == "discord.js" ]]; then
|
||||
mkdir -p "out/${PACKAGE}"
|
||||
mv "packages/${PACKAGE}/docs/docs.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.json"
|
||||
else
|
||||
mkdir -p "out/${PACKAGE}"
|
||||
mv "packages/${PACKAGE}/docs/docs.api.json" "out/${PACKAGE}/${GITHUB_REF_NAME}.api.json"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
|
||||
14
.github/workflows/issue-triage.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: 'Issue Labeler'
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
issue-triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: github/issue-labeler@v3.1
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
configuration-path: .github/issue-labeler.yml
|
||||
not-before: 2023-01-13T10:25:03.847Z
|
||||
enable-versioned-regex: 0
|
||||
20
.github/workflows/lighthouse-main.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: 'Lighthouse Audit (main)'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
lighthouse_audit_main:
|
||||
name: 'Lighthouse Audit (main)'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Audit production URLs with Lighthouse
|
||||
id: lighthouse_audit
|
||||
uses: treosh/lighthouse-ci-action@v9
|
||||
with:
|
||||
urls: |
|
||||
https://discordjs.dev
|
||||
https://guide.discordjs.dev
|
||||
uploadArtifacts: true
|
||||
temporaryPublicStorage: true
|
||||
88
.github/workflows/lighthouse.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: 'Lighthouse Audit'
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created, edited]
|
||||
jobs:
|
||||
lighthouse_audit:
|
||||
name: 'Lighthouse Audit'
|
||||
if: ${{ github.event.issue.pull_request }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Get Vercel preview URL
|
||||
id: get_preview_url
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const comment = context.payload.comment;
|
||||
const regex = /https:\/\/[a-z0-9-]+\.vercel\.app/g;
|
||||
const matches = comment.body.match(regex);
|
||||
let previewUrl = "";
|
||||
if (matches && matches.length) {
|
||||
previewUrl = matches[0];
|
||||
console.log('Preview url found:', previewUrl);
|
||||
}
|
||||
console.log("No preview url found.");
|
||||
core.setOutput('vercel_preview_url', previewUrl);
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add comment to PR
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: loading_comment_to_pr
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
number: ${{ github.event.issue.number }}
|
||||
header: lighthouse
|
||||
message: |
|
||||
Running Lighthouse audit...
|
||||
|
||||
- name: Checkout repository
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Audit preview URL with Lighthouse
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: lighthouse_audit
|
||||
uses: treosh/lighthouse-ci-action@v9
|
||||
with:
|
||||
urls: |
|
||||
${{ steps.get_preview_url.outputs.vercel_preview_url }}
|
||||
uploadArtifacts: true
|
||||
temporaryPublicStorage: true
|
||||
|
||||
- name: Format lighthouse score
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: format_lighthouse_score
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const result = ${{ steps.lighthouse_audit.outputs.manifest }}[0].summary
|
||||
const links = ${{ steps.lighthouse_audit.outputs.links }}
|
||||
const formatResult = (res) => Math.round((res * 100))
|
||||
Object.keys(result).forEach(key => result[key] = formatResult(result[key]))
|
||||
const score = res => res >= 90 ? '🟢' : res >= 50 ? '🟠' : '🔴'
|
||||
const comment = [
|
||||
`⚡️ [Lighthouse report](${Object.values(links)[0]}) for the changes in this PR:`,
|
||||
'| Category | Score |',
|
||||
'| --- | --- |',
|
||||
`| ${score(result.performance)} Performance | ${result.performance} |`,
|
||||
`| ${score(result.accessibility)} Accessibility | ${result.accessibility} |`,
|
||||
`| ${score(result['best-practices'])} Best practices | ${result['best-practices']} |`,
|
||||
`| ${score(result.seo)} SEO | ${result.seo} |`,
|
||||
`| ${score(result.pwa)} PWA | ${result.pwa} |`,
|
||||
' ',
|
||||
`*Lighthouse ran on [${Object.keys(links)[0]}](${Object.keys(links)[0]})*`
|
||||
].join('\n')
|
||||
core.setOutput("comment", comment);
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add comment to PR
|
||||
if: ${{ steps.get_preview_url.outputs.vercel_preview_url != '' }}
|
||||
id: comment_to_pr
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
number: ${{ github.event.issue.number }}
|
||||
header: lighthouse
|
||||
message: |
|
||||
${{ steps.format_lighthouse_score.outputs.comment }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
19
.github/workflows/lock.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Lock Ancient Issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 16 * * *'
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
issues: write
|
||||
concurrency:
|
||||
group: lock
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v4
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-inactive-days: 365
|
||||
issue-lock-reason: resolved
|
||||
process-only: issues
|
||||
6
.github/workflows/npm-auto-deprecate.yml
vendored
@@ -12,15 +12,15 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v16
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Deprecate versions
|
||||
run: 'yarn npm-deprecate --name "*dev*" --package @discordjs/brokers @discordjs/builders @discordjs/collection @discordjs/core 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/next @discordjs/proxy @discordjs/rest @discordjs/util @discordjs/voice @discordjs/ws'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
|
||||
4
.github/workflows/pr-triage.yml
vendored
@@ -11,7 +11,3 @@ jobs:
|
||||
with:
|
||||
repo-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
sync-labels: true
|
||||
|
||||
- name: Automatically assign reviewers
|
||||
if: github.event.action == 'opened'
|
||||
uses: kentaro-m/auto-assign-action@v1.2.3
|
||||
|
||||
13
.github/workflows/publish-dev-docker.yml
vendored
@@ -12,6 +12,17 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
@@ -19,7 +30,7 @@ jobs:
|
||||
run: echo ${{ secrets.DOCKER_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
- name: Build the image
|
||||
run: docker build -t discordjs/proxy:latest -f packages/proxy-container/Dockerfile .
|
||||
run: yarn docker build @discordjs/proxy-container -t discordjs/proxy:latest
|
||||
|
||||
- name: Push image to DockerHub
|
||||
run: docker push discordjs/proxy:latest
|
||||
|
||||
8
.github/workflows/publish-dev.yml
vendored
@@ -18,8 +18,12 @@ jobs:
|
||||
folder: 'collection'
|
||||
- package: '@discordjs/core'
|
||||
folder: 'core'
|
||||
- package: '@discordjs/formatters'
|
||||
folder: 'formatters'
|
||||
- package: 'discord.js'
|
||||
folder: 'discord.js'
|
||||
- package: '@discordjs/next'
|
||||
folder: 'next'
|
||||
- package: '@discordjs/proxy'
|
||||
folder: 'proxy'
|
||||
- package: '@discordjs/rest'
|
||||
@@ -39,10 +43,10 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v16
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Install dependencies
|
||||
|
||||
13
.github/workflows/publish-docker.yml
vendored
@@ -9,6 +9,17 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
@@ -16,7 +27,7 @@ jobs:
|
||||
run: echo ${{ secrets.DOCKER_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
- name: Build docker image
|
||||
run: docker build -t discordjs/proxy:latest -f packages/proxy-container/Dockerfile .
|
||||
run: yarn docker build @discordjs/proxy-container -t discordjs/proxy:latest
|
||||
|
||||
- name: Tag image with major
|
||||
run: docker tag discordjs/proxy discordjs/proxy:$(cut -d '.' -f1 <<< $(jq --raw-output '.version' packages/proxy-container/package.json))
|
||||
|
||||
39
.github/workflows/publish-release.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Publish Release
|
||||
on:
|
||||
release:
|
||||
types: [released]
|
||||
jobs:
|
||||
npm-publish:
|
||||
name: npm publish
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
if: github.repository_owner == 'discordjs'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
- name: Build dependencies
|
||||
run: yarn build
|
||||
|
||||
- name: Extract package and semver from tag
|
||||
id: extract-tag
|
||||
uses: ./packages/actions/src/formatTag
|
||||
with:
|
||||
tag: ${{ github.ref_name }}
|
||||
|
||||
- name: Publish package
|
||||
run: |
|
||||
yarn workspace ${{ steps.extract-tag.outputs.subpackage == 'true' && '@discordjs/' || '' }}${{ steps.extract-tag.outputs.package }} npm publish
|
||||
env:
|
||||
YARN_NPM_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }}
|
||||
7
.github/workflows/tests.yml
vendored
@@ -3,7 +3,8 @@ on:
|
||||
push:
|
||||
pull_request:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}
|
||||
# Group based on workflow name and PR if it exists, if no PR, let it run so carryforward flags work
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
tests:
|
||||
@@ -18,10 +19,10 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install node.js v16
|
||||
- name: Install node.js v18
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
uses: ./packages/actions/src/yarnCache
|
||||
|
||||
2
.gitignore
vendored
@@ -16,6 +16,7 @@ pids
|
||||
|
||||
# Dist
|
||||
dist/
|
||||
dist-docs/
|
||||
|
||||
# Miscellaneous
|
||||
.tmp/
|
||||
@@ -41,3 +42,4 @@ out/
|
||||
# Cache
|
||||
.prettiercache
|
||||
.eslintcache
|
||||
.vercel
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"*": "prettier --ignore-unknown --write",
|
||||
"{src/**,__tests__/**}.{mjs,js,cjs,ts,tsx,astro}": "eslint --ext .mjs,.js,.cjs,.ts,.tsx,.astro --fix",
|
||||
"{src/**,__tests__/**}.{mjs,js,cjs,ts,tsx}": "eslint --ext .mjs,.js,.cjs,.ts,.tsx --fix",
|
||||
"src/**.ts": "vitest related --run --config ../../vitest.config.ts"
|
||||
}
|
||||
|
||||
1
.prettierignore
Normal file
@@ -0,0 +1 @@
|
||||
CODEOWNERS
|
||||
1
.vscode/extensions.json
vendored
@@ -9,7 +9,6 @@
|
||||
"christian-kohler.npm-intellisense",
|
||||
"christian-kohler.path-intellisense",
|
||||
"antfu.unocss",
|
||||
"astro-build.astro-vscode",
|
||||
"unifiedjs.vscode-mdx"
|
||||
]
|
||||
}
|
||||
|
||||
3
.vscode/settings.json
vendored
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"eslint.workingDirectories": [{ "pattern": "./apps/*" }, { "pattern": "./packages/*" }],
|
||||
"eslint.validate": ["javascript", "javascriptreact", "astro", "typescript", "typescriptreact"],
|
||||
"prettier.documentSelectors": ["**/*.astro"],
|
||||
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
diff --git a/lib/TSDocConfigFile.js b/lib/TSDocConfigFile.js
|
||||
index caf3515d60fd386c5909db5a0aa8b4180b10d602..6fa4f1984b6ba6b3a7aecd05e54477ebf141af94 100644
|
||||
--- a/lib/TSDocConfigFile.js
|
||||
+++ b/lib/TSDocConfigFile.js
|
||||
@@ -31,8 +31,7 @@ const ajv_1 = __importDefault(require("ajv"));
|
||||
const jju = __importStar(require("jju"));
|
||||
const ajv = new ajv_1.default({ verbose: true });
|
||||
function initializeSchemaValidator() {
|
||||
- const jsonSchemaPath = resolve.sync('@microsoft/tsdoc/schemas/tsdoc.schema.json', { basedir: __dirname });
|
||||
- const jsonSchemaContent = fs.readFileSync(jsonSchemaPath).toString();
|
||||
+ const jsonSchemaContent = "{\"title\":\"TSDoc Configuration\",\"description\":\"Describes the TSDoc configuration for a TypeScript project\",\"type\":\"object\",\"properties\":{\"$schema\":{\"description\":\"Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.\",\"type\":\"string\"},\"extends\":{\"description\":\"Optionally specifies one or more JSON config files that will be combined with this file. This provides a way for standard settings to be shared across multiple projects. Important: The \\\"extends\\\" paths are resolved using NodeJS module resolution, so a path to a local file MUST be prefixed with \\\"./\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"noStandardTags\":{\"description\":\"By default, the config file loader will predefine all of the standardized TSDoc tags. To disable this and start with a completely empty configuration, set \\\"noStandardTags\\\"=true.\",\"type\":\"boolean\"},\"tagDefinitions\":{\"description\":\"Additional tags to support when parsing documentation comments with TSDoc.\",\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/tsdocTagDefinition\"}},\"supportedHtmlElements\":{\"description\":\"The HTML element names that are supported in this configuration. Used in conjunction with the \\\"reportUnsupportedHtmlElements\\\" setting.\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^[a-zA-Z0-9-]+$\"}},\"reportUnsupportedHtmlElements\":{\"description\":\"Whether an error should be reported when an unsupported HTML element is encountered in a doc comment. Defaults to \\\"true\\\" if the \\\"supportedHtmlElements\\\" field is present in this file, \\\"false\\\" if not.\",\"type\":\"boolean\"},\"supportForTags\":{\"description\":\"A collection of key/value pairs. The key is a TSDoc tag name (e.g. \\\"@myTag\\\") that must be defined in this configuration. The value is a boolean indicating whether the tag is supported. The TSDoc parser may report warnings when unsupported tags are encountered. If \\\"supportForTags\\\" is specified for at least one tag, then the \\\"reportUnsupportedTags\\\" validation check is enabled by default.\",\"type\":\"object\",\"patternProperties\":{\"@[a-zA-Z][a-zA-Z0-9]*$\":{\"type\":\"boolean\"}},\"additionalItems\":false}},\"required\":[\"$schema\"],\"additionalProperties\":false,\"definitions\":{\"tsdocTagDefinition\":{\"description\":\"Configuration for a custom supported TSDoc tag.\",\"type\":\"object\",\"properties\":{\"tagName\":{\"description\":\"Name of the custom tag. TSDoc tag names start with an at-sign (@) followed by ASCII letters using camelCase capitalization.\",\"type\":\"string\"},\"syntaxKind\":{\"description\":\"Syntax kind of the custom tag. \\\"inline\\\" means that this tag can appear inside other documentation sections (example: {@link}). \\\"block\\\" means that this tag starts a new documentation section (example: @remarks). \\\"modifier\\\" means that this tag's presence indicates an aspect of the associated API item (example: @internal).\",\"type\":\"string\",\"enum\":[\"inline\",\"block\",\"modifier\"]},\"allowMultiple\":{\"description\":\"If true, then this tag may appear multiple times in a doc comment. By default, a tag may only appear once.\",\"type\":\"boolean\"}},\"required\":[\"tagName\",\"syntaxKind\"],\"additionalProperties\":false}}}";
|
||||
const jsonSchema = jju.parse(jsonSchemaContent, { mode: 'cjson' });
|
||||
return ajv.compile(jsonSchema);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
diff --git a/lib/TSDocConfigFile.js b/lib/TSDocConfigFile.js
|
||||
index caf3515d60fd386c5909db5a0aa8b4180b10d602..5f7cfed7611e3fe660b5265ff99c5da0beb7caec 100644
|
||||
--- a/lib/TSDocConfigFile.js
|
||||
+++ b/lib/TSDocConfigFile.js
|
||||
@@ -31,8 +31,7 @@ const ajv_1 = __importDefault(require("ajv"));
|
||||
const jju = __importStar(require("jju"));
|
||||
const ajv = new ajv_1.default({ verbose: true });
|
||||
function initializeSchemaValidator() {
|
||||
- const jsonSchemaPath = resolve.sync('@microsoft/tsdoc/schemas/tsdoc.schema.json', { basedir: __dirname });
|
||||
- const jsonSchemaContent = fs.readFileSync(jsonSchemaPath).toString();
|
||||
+ const jsonSchemaContent = '{\"title\":\"TSDoc Configuration\",\"description\":\"Describes the TSDoc configuration for a TypeScript project\",\"type\":\"object\",\"properties\":{\"$schema\":{\"description\":\"Part of the JSON Schema standard, this optional keyword declares the URL of the schema that the file conforms to. Editors may download the schema and use it to perform syntax highlighting.\",\"type\":\"string\"},\"extends\":{\"description\":\"Optionally specifies one or more JSON config files that will be combined with this file. This provides a way for standard settings to be shared across multiple projects. Important: The \\\"extends\\\" paths are resolved using NodeJS module resolution, so a path to a local file MUST be prefixed with \\\".\/\\\".\",\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"noStandardTags\":{\"description\":\"By default, the config file loader will predefine all of the standardized TSDoc tags. To disable this and start with a completely empty configuration, set \\\"noStandardTags\\\"=true.\",\"type\":\"boolean\"},\"tagDefinitions\":{\"description\":\"Additional tags to support when parsing documentation comments with TSDoc.\",\"type\":\"array\",\"items\":{\"$ref\":\"#\/definitions\/tsdocTagDefinition\"}},\"supportedHtmlElements\":{\"description\":\"The HTML element names that are supported in this configuration. Used in conjunction with the \\\"reportUnsupportedHtmlElements\\\" setting.\",\"type\":\"array\",\"items\":{\"type\":\"string\",\"pattern\":\"^[a-zA-Z0-9-]+$\"}},\"reportUnsupportedHtmlElements\":{\"description\":\"Whether an error should be reported when an unsupported HTML element is encountered in a doc comment. Defaults to \\\"true\\\" if the \\\"supportedHtmlElements\\\" field is present in this file, \\\"false\\\" if not.\",\"type\":\"boolean\"},\"supportForTags\":{\"description\":\"A collection of key\/value pairs. The key is a TSDoc tag name (e.g. \\\"@myTag\\\") that must be defined in this configuration. The value is a boolean indicating whether the tag is supported. The TSDoc parser may report warnings when unsupported tags are encountered. If \\\"supportForTags\\\" is specified for at least one tag, then the \\\"reportUnsupportedTags\\\" validation check is enabled by default.\",\"type\":\"object\",\"patternProperties\":{\"@[a-zA-Z][a-zA-Z0-9]*$\":{\"type\":\"boolean\"}},\"additionalItems\":false}},\"required\":[\"$schema\"],\"additionalProperties\":false,\"definitions\":{\"tsdocTagDefinition\":{\"description\":\"Configuration for a custom supported TSDoc tag.\",\"type\":\"object\",\"properties\":{\"tagName\":{\"description\":\"Name of the custom tag. TSDoc tag names start with an at-sign (@) followed by ASCII letters using camelCase capitalization.\",\"type\":\"string\"},\"syntaxKind\":{\"description\":\"Syntax kind of the custom tag. \\\"inline\\\" means that this tag can appear inside other documentation sections (example: {@link}). \\\"block\\\" means that this tag starts a new documentation section (example: @remarks). \\\"modifier\\\" means that this tag\'s presence indicates an aspect of the associated API item (example: @internal).\",\"type\":\"string\",\"enum\":[\"inline\",\"block\",\"modifier\"]},\"allowMultiple\":{\"description\":\"If true, then this tag may appear multiple times in a doc comment. By default, a tag may only appear once.\",\"type\":\"boolean\"}},\"required\":[\"tagName\",\"syntaxKind\"],\"additionalProperties\":false}}}';
|
||||
const jsonSchema = jju.parse(jsonSchemaContent, { mode: 'cjson' });
|
||||
return ajv.compile(jsonSchema);
|
||||
}
|
||||
18
.yarn/patches/yaml-npm-2.2.2-6e3cddb343.patch
Normal file
@@ -0,0 +1,18 @@
|
||||
diff --git a/package.json b/package.json
|
||||
index fc35658a40f9ba3e3513c459ba9f4f6e1b3f59f5..bc35eda66f270c95ba52e721cb6976fd61622c58 100644
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -26,11 +26,13 @@
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
+ "types": "./dist/index.d.ts",
|
||||
"node": "./dist/index.js",
|
||||
"default": "./browser/index.js"
|
||||
},
|
||||
"./package.json": "./package.json",
|
||||
"./util": {
|
||||
+ "types": "./dist/util.d.ts",
|
||||
"node": "./dist/util.js",
|
||||
"default": "./browser/dist/util.js"
|
||||
}
|
||||
257
.yarn/plugins/@yarnpkg/plugin-docker-build.cjs
vendored
Normal file
@@ -0,0 +1,257 @@
|
||||
/* eslint-disable */
|
||||
module.exports = {
|
||||
name: '@yarnpkg/plugin-docker-build',
|
||||
factory: function (require) {
|
||||
var plugin;
|
||||
(() => {
|
||||
'use strict';
|
||||
var t = {
|
||||
d: (e, o) => {
|
||||
for (var r in o) t.o(o, r) && !t.o(e, r) && Object.defineProperty(e, r, { enumerable: !0, get: o[r] });
|
||||
},
|
||||
o: (t, e) => Object.prototype.hasOwnProperty.call(t, e),
|
||||
r: (t) => {
|
||||
'undefined' != typeof Symbol &&
|
||||
Symbol.toStringTag &&
|
||||
Object.defineProperty(t, Symbol.toStringTag, { value: 'Module' }),
|
||||
Object.defineProperty(t, '__esModule', { value: !0 });
|
||||
},
|
||||
},
|
||||
e = {};
|
||||
t.r(e), t.d(e, { default: () => u });
|
||||
const o = require('@yarnpkg/cli'),
|
||||
r = require('clipanion'),
|
||||
i = require('@yarnpkg/core'),
|
||||
a = require('@yarnpkg/plugin-patch'),
|
||||
n = require('@yarnpkg/fslib');
|
||||
const s = require('@yarnpkg/plugin-pack');
|
||||
async function c({ workspace: t, destination: e, report: o }) {
|
||||
await s.packUtils.prepareForPack(t, { report: o }, async () => {
|
||||
const r = await s.packUtils.genPackList(t),
|
||||
a = i.Report.progressViaCounter(r.length),
|
||||
c = o.reportProgress(a);
|
||||
try {
|
||||
for (const i of r) {
|
||||
const r = n.ppath.join(t.cwd, i),
|
||||
s = n.ppath.join(e, t.relativeCwd, i);
|
||||
o.reportInfo(null, i), await n.xfs.copyPromise(s, r, { overwrite: !0 }), a.tick();
|
||||
}
|
||||
} finally {
|
||||
c.stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
function p(t, e) {
|
||||
const o = (0, n.toFilename)(e);
|
||||
return n.ppath.isAbsolute(o) ? n.ppath.relative(t, o) : o;
|
||||
}
|
||||
const l = /^builtin<([^>]+)>$/;
|
||||
var d = function (t, e, o, r) {
|
||||
var i,
|
||||
a = arguments.length,
|
||||
n = a < 3 ? e : null === r ? (r = Object.getOwnPropertyDescriptor(e, o)) : r;
|
||||
if ('object' == typeof Reflect && 'function' == typeof Reflect.decorate) n = Reflect.decorate(t, e, o, r);
|
||||
else
|
||||
for (var s = t.length - 1; s >= 0; s--)
|
||||
(i = t[s]) && (n = (a < 3 ? i(n) : a > 3 ? i(e, o, n) : i(e, o)) || n);
|
||||
return a > 3 && n && Object.defineProperty(e, o, n), n;
|
||||
};
|
||||
class f extends o.BaseCommand {
|
||||
constructor() {
|
||||
super(...arguments), (this.args = []);
|
||||
}
|
||||
async execute() {
|
||||
const t = await i.Configuration.find(this.context.cwd, this.context.plugins),
|
||||
{ project: e } = await i.Project.find(t, this.context.cwd),
|
||||
o = e.getWorkspaceByIdent(i.structUtils.parseIdent(this.workspaceName)),
|
||||
r = (function ({
|
||||
project: t,
|
||||
workspaces: e,
|
||||
production: o = !1,
|
||||
scopes: r = o ? ['dependencies'] : i.Manifest.hardDependencies,
|
||||
}) {
|
||||
const a = new Set([...e]);
|
||||
for (const e of a)
|
||||
for (const o of r) {
|
||||
const r = e.manifest.getForScope(o).values();
|
||||
for (const e of r) {
|
||||
const o = t.tryWorkspaceByDescriptor(e);
|
||||
o && a.add(o);
|
||||
}
|
||||
}
|
||||
for (const e of t.workspaces)
|
||||
a.has(e)
|
||||
? o && e.manifest.devDependencies.clear()
|
||||
: (e.manifest.dependencies.clear(),
|
||||
e.manifest.devDependencies.clear(),
|
||||
e.manifest.peerDependencies.clear());
|
||||
return a;
|
||||
})({ project: e, workspaces: [o], production: this.production }),
|
||||
s = await (async function (t, e = 'Dockerfile') {
|
||||
const o = (0, n.toFilename)(e);
|
||||
if (n.ppath.isAbsolute(o)) return o;
|
||||
const r = [n.ppath.join(t.cwd, o), n.ppath.join(t.project.cwd, o)];
|
||||
for (const t of r) if (await n.xfs.existsPromise(t)) return t;
|
||||
throw new Error('Dockerfile is required');
|
||||
})(o, this.dockerFilePath),
|
||||
d = await i.Cache.find(t);
|
||||
return (
|
||||
await i.StreamReport.start(
|
||||
{ configuration: t, stdout: this.context.stdout, includeLogs: !this.context.quiet },
|
||||
async (t) => {
|
||||
await t.startTimerPromise('Resolution Step', async () => {
|
||||
await e.resolveEverything({ report: t, cache: d });
|
||||
}),
|
||||
await t.startTimerPromise('Fetch Step', async () => {
|
||||
await e.fetchEverything({ report: t, cache: d });
|
||||
}),
|
||||
await n.xfs.mktempPromise(async (o) => {
|
||||
const f = n.ppath.join(o, (0, n.toFilename)('manifests')),
|
||||
u = n.ppath.join(o, (0, n.toFilename)('packs'));
|
||||
await t.startTimerPromise('Copy files', async () => {
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = e.configuration.get('rcFilename');
|
||||
o.reportInfo(null, r),
|
||||
await n.xfs.copyPromise(n.ppath.join(t, r), n.ppath.join(e.cwd, r), { overwrite: !0 });
|
||||
})({ destination: f, project: e, report: t }),
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = n.ppath.join((0, n.toFilename)('.yarn'), (0, n.toFilename)('plugins'));
|
||||
o.reportInfo(null, r),
|
||||
await n.xfs.copyPromise(n.ppath.join(t, r), n.ppath.join(e.cwd, r), { overwrite: !0 });
|
||||
})({ destination: f, project: e, report: t }),
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = e.configuration.get('yarnPath'),
|
||||
i = n.ppath.relative(e.cwd, r),
|
||||
a = n.ppath.join(t, i);
|
||||
o.reportInfo(null, i), await n.xfs.copyPromise(a, r, { overwrite: !0 });
|
||||
})({ destination: f, project: e, report: t }),
|
||||
await (async function ({ destination: t, workspaces: e, report: o }) {
|
||||
for (const r of e) {
|
||||
const e = n.ppath.join(r.relativeCwd, i.Manifest.fileName),
|
||||
a = n.ppath.join(t, e),
|
||||
s = {};
|
||||
r.manifest.exportTo(s),
|
||||
o.reportInfo(null, e),
|
||||
await n.xfs.mkdirpPromise(n.ppath.dirname(a)),
|
||||
await n.xfs.writeJsonPromise(a, s);
|
||||
}
|
||||
})({ destination: f, workspaces: e.workspaces, report: t }),
|
||||
await (async function ({ destination: t, report: e, project: o, parseDescriptor: r }) {
|
||||
const a = new Set();
|
||||
for (const s of o.storedDescriptors.values()) {
|
||||
const c = r(
|
||||
i.structUtils.isVirtualDescriptor(s) ? i.structUtils.devirtualizeDescriptor(s) : s,
|
||||
);
|
||||
if (!c) continue;
|
||||
const { parentLocator: p, paths: d } = c;
|
||||
for (const r of d) {
|
||||
if (l.test(r)) continue;
|
||||
if (n.ppath.isAbsolute(r)) continue;
|
||||
const i = o.getWorkspaceByLocator(p),
|
||||
s = n.ppath.join(i.relativeCwd, r);
|
||||
if (a.has(s)) continue;
|
||||
a.add(s);
|
||||
const c = n.ppath.join(i.cwd, r),
|
||||
d = n.ppath.join(t, s);
|
||||
e.reportInfo(null, s),
|
||||
await n.xfs.mkdirpPromise(n.ppath.dirname(d)),
|
||||
await n.xfs.copyFilePromise(c, d);
|
||||
}
|
||||
}
|
||||
})({
|
||||
destination: f,
|
||||
report: t,
|
||||
project: e,
|
||||
parseDescriptor: (t) => {
|
||||
if (t.range.startsWith('exec:')) {
|
||||
const e = (function (t) {
|
||||
const { params: e, selector: o } = i.structUtils.parseRange(t),
|
||||
r = n.npath.toPortablePath(o);
|
||||
return {
|
||||
parentLocator:
|
||||
e && 'string' == typeof e.locator ? i.structUtils.parseLocator(e.locator) : null,
|
||||
path: r,
|
||||
};
|
||||
})(t.range);
|
||||
if (!e || !e.parentLocator) return;
|
||||
return { parentLocator: e.parentLocator, paths: [e.path] };
|
||||
}
|
||||
if (t.range.startsWith('patch:')) {
|
||||
const { parentLocator: e, patchPaths: o } = a.patchUtils.parseDescriptor(t);
|
||||
if (!e) return;
|
||||
return { parentLocator: e, paths: o };
|
||||
}
|
||||
},
|
||||
}),
|
||||
await (async function ({ destination: t, project: e, cache: o, report: r }) {
|
||||
for (const i of o.markedFiles) {
|
||||
const o = n.ppath.relative(e.cwd, i);
|
||||
(await n.xfs.existsPromise(i)) &&
|
||||
(r.reportInfo(null, o), await n.xfs.copyPromise(n.ppath.join(t, o), i));
|
||||
}
|
||||
})({ destination: f, project: e, cache: d, report: t }),
|
||||
await (async function ({ destination: t, project: e, report: o }) {
|
||||
const r = (0, n.toFilename)(e.configuration.get('lockfileFilename')),
|
||||
i = n.ppath.join(t, r);
|
||||
o.reportInfo(null, r),
|
||||
await n.xfs.mkdirpPromise(n.ppath.dirname(i)),
|
||||
await n.xfs.writeFilePromise(i, e.generateLockfile());
|
||||
})({ destination: f, project: e, report: t }),
|
||||
this.copyFiles &&
|
||||
this.copyFiles.length &&
|
||||
(await (async function ({ destination: t, files: e, dockerFilePath: o, report: r }) {
|
||||
const i = n.ppath.dirname(o);
|
||||
for (const o of e) {
|
||||
const e = p(i, o),
|
||||
a = n.ppath.join(i, e),
|
||||
s = n.ppath.join(t, e);
|
||||
r.reportInfo(null, e), await n.xfs.copyPromise(s, a);
|
||||
}
|
||||
})({ destination: f, files: this.copyFiles, dockerFilePath: s, report: t }));
|
||||
});
|
||||
for (const e of r) {
|
||||
const o = e.manifest.name ? i.structUtils.stringifyIdent(e.manifest.name) : '';
|
||||
await t.startTimerPromise('Pack workspace ' + o, async () => {
|
||||
await c({ workspace: e, report: t, destination: u });
|
||||
});
|
||||
}
|
||||
await i.execUtils.pipevp('docker', ['build', ...this.args, '-f', s, '.'], {
|
||||
cwd: o,
|
||||
strict: !0,
|
||||
stdin: this.context.stdin,
|
||||
stdout: this.context.stdout,
|
||||
stderr: this.context.stderr,
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
).exitCode();
|
||||
}
|
||||
}
|
||||
(f.usage = r.Command.Usage({
|
||||
category: 'Docker-related commands',
|
||||
description: 'Build a Docker image for a workspace',
|
||||
details:
|
||||
'\n This command will build a efficient Docker image which only contains necessary dependencies for the specified workspace.\n\n You have to create a Dockerfile in your workspace or your project. You can also specify the path to Dockerfile using the "-f, --file" option.\n\n Additional arguments can be passed to "docker build" directly, please check the Docker docs for more info: https://docs.docker.com/engine/reference/commandline/build/\n\n You can copy additional files or folders to a Docker image using the "--copy" option. This is useful for secret keys or configuration files. The files will be copied to "manifests" folder. The path can be either a path relative to the Dockerfile or an absolute path.\n ',
|
||||
examples: [
|
||||
['Build a Docker image for a workspace', 'yarn docker build @foo/bar'],
|
||||
['Pass additional arguments to docker build command', 'yarn docker build @foo/bar -t image-tag'],
|
||||
[
|
||||
'Copy additional files to a Docker image',
|
||||
'yarn docker build --copy secret.key --copy config.json @foo/bar',
|
||||
],
|
||||
['Install production dependencies only', 'yarn docker build --production @foo/bar'],
|
||||
],
|
||||
})),
|
||||
d([r.Command.String()], f.prototype, 'workspaceName', void 0),
|
||||
d([r.Command.Proxy()], f.prototype, 'args', void 0),
|
||||
d([r.Command.String('-f,--file')], f.prototype, 'dockerFilePath', void 0),
|
||||
d([r.Command.Array('--copy')], f.prototype, 'copyFiles', void 0),
|
||||
d([r.Command.Boolean('--production')], f.prototype, 'production', void 0),
|
||||
d([r.Command.Path('docker', 'build')], f.prototype, 'execute', null);
|
||||
const u = { commands: [f] };
|
||||
plugin = e;
|
||||
})();
|
||||
return plugin;
|
||||
},
|
||||
};
|
||||
801
.yarn/releases/yarn-3.2.4.cjs
vendored
873
.yarn/releases/yarn-3.5.0.cjs
vendored
Executable file
15
.yarnrc.yml
@@ -2,10 +2,17 @@ nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||
spec: "@yarnpkg/plugin-interactive-tools"
|
||||
spec: '@yarnpkg/plugin-interactive-tools'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
spec: '@yarnpkg/plugin-workspace-tools'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: "@yarnpkg/plugin-version"
|
||||
spec: '@yarnpkg/plugin-version'
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-docker-build.cjs
|
||||
spec: 'https://github.com/Dcard/yarn-plugins/releases/latest/download/plugin-docker-build.js'
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.4.cjs
|
||||
yarnPath: .yarn/releases/yarn-3.5.0.cjs
|
||||
|
||||
packageExtensions:
|
||||
'@storybook/core-common@*':
|
||||
dependencies:
|
||||
'@storybook/react-vite': '7.0.7'
|
||||
|
||||
119
README.md
@@ -18,96 +18,28 @@
|
||||
|
||||
## About
|
||||
|
||||
discord.js is a powerful [Node.js](https://nodejs.org) module that allows you to easily interact with the
|
||||
[Discord API](https://discord.com/developers/docs/intro).
|
||||
This repository contains multiple packages with separate [releases][github-releases]. You can find the assembled Discord API wrapper at [`discord.js`][source]. It is a powerful [Node.js](https://nodejs.org/en) module that allows you to easily interact with the [Discord API](https://discord.com/developers/docs/intro).
|
||||
|
||||
- Object-oriented
|
||||
- Predictable abstractions
|
||||
- Performant
|
||||
- 100% coverage of the Discord API
|
||||
## Packages
|
||||
|
||||
## Installation
|
||||
|
||||
**Node.js 16.9.0 or newer is required.**
|
||||
|
||||
```sh-session
|
||||
npm install discord.js
|
||||
yarn add discord.js
|
||||
pnpm add discord.js
|
||||
```
|
||||
|
||||
### Optional packages
|
||||
|
||||
- [zlib-sync](https://www.npmjs.com/package/zlib-sync) for WebSocket data compression and inflation (`npm install zlib-sync`)
|
||||
- [erlpack](https://github.com/discord/erlpack) for significantly faster WebSocket data (de)serialisation (`npm install discord/erlpack`)
|
||||
- [bufferutil](https://www.npmjs.com/package/bufferutil) for a much faster WebSocket connection (`npm install bufferutil`)
|
||||
- [utf-8-validate](https://www.npmjs.com/package/utf-8-validate) in combination with `bufferutil` for much faster WebSocket processing (`npm install utf-8-validate`)
|
||||
- [@discordjs/voice](https://www.npmjs.com/package/@discordjs/voice) for interacting with the Discord Voice API (`npm install @discordjs/voice`)
|
||||
|
||||
## Example usage
|
||||
|
||||
Install discord.js:
|
||||
|
||||
```sh-session
|
||||
npm install discord.js
|
||||
yarn add discord.js
|
||||
pnpm add discord.js
|
||||
```
|
||||
|
||||
Register a slash command against the Discord API:
|
||||
|
||||
```js
|
||||
const { REST, Routes } = require('discord.js');
|
||||
|
||||
const commands = [
|
||||
{
|
||||
name: 'ping',
|
||||
description: 'Replies with Pong!',
|
||||
},
|
||||
];
|
||||
|
||||
const rest = new REST({ version: '10' }).setToken(TOKEN);
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
console.log('Started refreshing application (/) commands.');
|
||||
|
||||
await rest.put(Routes.applicationCommands(CLIENT_ID), { body: commands });
|
||||
|
||||
console.log('Successfully reloaded application (/) commands.');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
})();
|
||||
```
|
||||
|
||||
Afterwards we can create a quite simple example bot:
|
||||
|
||||
```js
|
||||
const { Client, GatewayIntentBits } = require('discord.js');
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.on('ready', () => {
|
||||
console.log(`Logged in as ${client.user.tag}!`);
|
||||
});
|
||||
|
||||
client.on('interactionCreate', async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
if (interaction.commandName === 'ping') {
|
||||
await interaction.reply('Pong!');
|
||||
}
|
||||
});
|
||||
|
||||
client.login(TOKEN);
|
||||
```
|
||||
- `discord.js` ([source][source]) - A powerful Node.js module for interacting with the Discord API
|
||||
- `@discordjs/brokers` ([source][brokers-source]) - A collection of brokers for use with discord.js
|
||||
- `@discordjs/builders` ([source][builders-source]) - A utility package for easily building Discord API payloads
|
||||
- `@discordjs/collection` ([source][collection-source]) - A powerful utility data structure
|
||||
- `@discordjs/core` ([source][core-source]) - A thinly abstracted wrapper around the core components of the Discord API
|
||||
- `@discordjs/formatters` ([source][formatters-source]) - A collection of functions for formatting strings
|
||||
- `@discordjs/proxy` ([source][proxy-source]) - A wrapper around `@discordjs/rest` for running an HTTP proxy
|
||||
- `@discordjs/rest` ([source][rest-source]) - A module for interacting with the Discord REST API
|
||||
- `@discordjs/voice` ([source][voice-source]) - A module for interacting with the Discord Voice API
|
||||
- `@discordjs/util` ([source][util-source]) - A collection of utility functions
|
||||
- `@discordjs/ws` ([source][ws-source]) - A wrapper around Discord's gateway
|
||||
|
||||
## Links
|
||||
|
||||
- [Website][website] ([source][website-source])
|
||||
- [Documentation][documentation]
|
||||
- [Guide][guide] ([source][guide-source])
|
||||
See also the [Update Guide][guide-update], including updated and removed items in the library.
|
||||
Also see the v13 to v14 [Update Guide][guide-update], which includes updated and removed items from the library.
|
||||
- [discord.js Discord server][discord]
|
||||
- [Discord API Discord server][discord-api]
|
||||
- [GitHub][source]
|
||||
@@ -120,18 +52,15 @@ client.login(TOKEN);
|
||||
|
||||
## Contributing
|
||||
|
||||
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
|
||||
[documentation][documentation].
|
||||
See [the contribution guide][contributing] if you'd like to submit a PR.
|
||||
Please read through our [contribution guidelines][contributing] before starting a pull request. We welcome contributions of all kinds, not just code! If you're stuck for ideas, look for the [good first issue][good-first-issue] label on issues in the repository. If you have any questions about the project, feel free to ask them on [Discord][discord]. Before creating your own issue or pull request, always check to see if one already exists! Don't rush contributions, take your time and ensure you're doing it correctly.
|
||||
|
||||
## Help
|
||||
|
||||
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
|
||||
nudge in the right direction, please don't hesitate to join our official [discord.js Server][discord].
|
||||
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle nudge in the right direction, please join our [Discord server][discord].
|
||||
|
||||
[website]: https://discord.js.org/
|
||||
[website]: https://discord.js.org
|
||||
[website-source]: https://github.com/discordjs/discord.js/tree/main/apps/website
|
||||
[documentation]: https://discord.js.org/#/docs
|
||||
[documentation]: https://discord.js.org/docs
|
||||
[guide]: https://discordjs.guide/
|
||||
[guide-source]: https://github.com/discordjs/guide
|
||||
[guide-update]: https://discordjs.guide/additional-info/changes-in-v14.html
|
||||
@@ -143,3 +72,15 @@ nudge in the right direction, please don't hesitate to join our official [discor
|
||||
[rpc]: https://www.npmjs.com/package/discord-rpc
|
||||
[rpc-source]: https://github.com/discordjs/RPC
|
||||
[contributing]: https://github.com/discordjs/discord.js/blob/main/.github/CONTRIBUTING.md
|
||||
[github-releases]: https://github.com/discordjs/discord.js/releases
|
||||
[brokers-source]: https://github.com/discordjs/discord.js/tree/main/packages/brokers
|
||||
[builders-source]: https://github.com/discordjs/discord.js/tree/main/packages/builders
|
||||
[collection-source]: https://github.com/discordjs/discord.js/tree/main/packages/collection
|
||||
[core-source]: https://github.com/discordjs/discord.js/tree/main/packages/core
|
||||
[formatters-source]: https://github.com/discordjs/discord.js/tree/main/packages/formatters
|
||||
[proxy-source]: https://github.com/discordjs/discord.js/tree/main/packages/proxy
|
||||
[rest-source]: https://github.com/discordjs/discord.js/tree/main/packages/rest
|
||||
[voice-source]: https://github.com/discordjs/discord.js/tree/main/packages/voice
|
||||
[util-source]: https://github.com/discordjs/discord.js/tree/main/packages/util
|
||||
[ws-source]: https://github.com/discordjs/discord.js/tree/main/packages/ws
|
||||
[good-first-issue]: https://github.com/discordjs/discord.js/contribute
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
*
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
*/
|
||||
"mainEntryPointFilePath": "<projectFolder>/dist/index.d.ts",
|
||||
"mainEntryPointFilePath": "<projectFolder>/dist-docs/index.d.ts",
|
||||
|
||||
/**
|
||||
* A list of NPM package names whose exports should be treated as part of this package.
|
||||
@@ -88,9 +88,44 @@
|
||||
*
|
||||
* DEFAULT VALUE: no overrideTsconfig section
|
||||
*/
|
||||
// "overrideTsconfig": {
|
||||
// . . .
|
||||
// }
|
||||
"overrideTsconfig": {
|
||||
// Type Checking
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strict": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
|
||||
// Modules
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
|
||||
// Emit
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"importHelpers": true,
|
||||
"inlineSources": true,
|
||||
"newLine": "lf",
|
||||
"noEmitHelpers": true,
|
||||
"outDir": "dist",
|
||||
"removeComments": false,
|
||||
"sourceMap": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
||||
// Language and Environment
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["ESNext"],
|
||||
"target": "ES2021",
|
||||
"useDefineForClassFields": true
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
@@ -181,7 +216,7 @@
|
||||
/**
|
||||
* (REQUIRED) Whether to generate the .d.ts rollup file.
|
||||
*/
|
||||
"enabled": false
|
||||
"enabled": true,
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated without any trimming.
|
||||
@@ -195,7 +230,7 @@
|
||||
* SUPPORTED TOKENS: <projectFolder>, <packageName>, <unscopedPackageName>
|
||||
* DEFAULT VALUE: "<projectFolder>/dist/<unscopedPackageName>.d.ts"
|
||||
*/
|
||||
// "untrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>.d.ts",
|
||||
"untrimmedFilePath": "<projectFolder>/dist-docs/index.d.ts"
|
||||
|
||||
/**
|
||||
* Specifies the output path for a .d.ts rollup file to be generated with trimming for an "alpha" release.
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json", "neon/react", "neon/astro", "neon/prettier"],
|
||||
"extends": ["../../.eslintrc.json", "neon/react", "neon/next", "neon/edge", "@unocss", "neon/prettier"],
|
||||
"settings": {
|
||||
"react": {
|
||||
"version": "detect"
|
||||
}
|
||||
},
|
||||
"rules": {
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".tsx", ".astro"] }]
|
||||
"react/react-in-jsx-scope": 0,
|
||||
"react/jsx-filename-extension": [1, { "extensions": [".tsx"] }]
|
||||
}
|
||||
}
|
||||
|
||||
5
apps/guide/.gitignore
vendored
@@ -13,13 +13,13 @@ pids
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env*.local
|
||||
|
||||
# Dist
|
||||
dist/
|
||||
typings/
|
||||
.cache/
|
||||
build/
|
||||
api/
|
||||
src/styles/unocss.css
|
||||
.next/
|
||||
|
||||
@@ -28,3 +28,6 @@ src/styles/unocss.css
|
||||
coverage/
|
||||
.vercel
|
||||
public/searchIndex
|
||||
.vscode
|
||||
lighthouse-results/
|
||||
.contentlayer
|
||||
|
||||
@@ -1,8 +1 @@
|
||||
module.exports = {
|
||||
...require('../../.prettierrc.json'),
|
||||
plugins: [
|
||||
'prettier-plugin-astro',
|
||||
'prettier-plugin-tailwindcss', // MUST come last
|
||||
],
|
||||
pluginSearchDirs: false,
|
||||
};
|
||||
module.exports = require('../../.prettierrc.json');
|
||||
|
||||
@@ -13,12 +13,16 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
## About
|
||||
|
||||
The official guide for discord.js, made to help you get started easily with the library.
|
||||
|
||||
## Links
|
||||
|
||||
- [Website][website] ([source][website-source])
|
||||
- [Documentation][documentation]
|
||||
- [Guide][guide] ([source][guide-source])
|
||||
See also the [Update Guide][guide-update], including updated and removed items in the library.
|
||||
Also see the v13 to v14 [Update Guide][guide-update], which includes updated and removed items from the library.
|
||||
- [discord.js Discord server][discord]
|
||||
- [Discord API Discord server][discord-api]
|
||||
- [GitHub][source]
|
||||
@@ -26,16 +30,17 @@
|
||||
|
||||
## Contributing
|
||||
|
||||
Before creating an issue, please ensure that it hasn't already been reported/suggested, and double-check the
|
||||
[documentation][documentation].
|
||||
See [the contribution guide][contributing] if you'd like to submit a PR.
|
||||
|
||||
## Help
|
||||
|
||||
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle
|
||||
nudge in the right direction, please don't hesitate to join our official [discord.js Server][discord].
|
||||
If you don't understand something in the documentation, you are experiencing problems, or you just need a gentle nudge in the right direction, please don't hesitate to join our official [discord.js Server][discord].
|
||||
|
||||
[website]: https://discord.js.org/
|
||||
[website]: https://discord.js.org
|
||||
[website-source]: https://github.com/discordjs/discord.js/tree/main/apps/website
|
||||
[documentation]: https://discord.js.org/
|
||||
[documentation]: https://discord.js.org/docs
|
||||
[guide]: https://discordjs.guide/
|
||||
[guide-source]: https://github.com/discordjs/guide
|
||||
[guide-update]: https://discordjs.guide/additional-info/changes-in-v14.html
|
||||
|
||||
@@ -1,117 +0,0 @@
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
import image from '@astrojs/image';
|
||||
import mdx from '@astrojs/mdx';
|
||||
import prefetch from '@astrojs/prefetch';
|
||||
import react from '@astrojs/react';
|
||||
import { remarkCodeHike } from '@code-hike/mdx';
|
||||
import { defineConfig } from 'astro/config';
|
||||
import compress from 'astro-compress';
|
||||
import critters from 'astro-critters';
|
||||
import { type Node, toString } from 'hast-util-to-string';
|
||||
import { h } from 'hastscript';
|
||||
import { escape } from 'html-escaper';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import shikiThemeDarkPlus from 'shiki/themes/dark-plus.json' assert { type: 'json' };
|
||||
import Unocss from 'unocss/astro';
|
||||
|
||||
const LinkIcon = h(
|
||||
'svg',
|
||||
{
|
||||
width: '1rem',
|
||||
height: '1rem',
|
||||
viewBox: '0 0 24 24',
|
||||
fill: 'none',
|
||||
stroke: 'currentColor',
|
||||
strokeWidth: '2',
|
||||
strokeLinecap: 'round',
|
||||
strokeLinejoin: 'round',
|
||||
},
|
||||
h('path', {
|
||||
// eslint-disable-next-line id-length
|
||||
d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',
|
||||
}),
|
||||
h('path', {
|
||||
// eslint-disable-next-line id-length
|
||||
d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',
|
||||
}),
|
||||
);
|
||||
|
||||
const createSROnlyLabel = (text: string) => {
|
||||
const node = h('span.sr-only', `Section titled ${escape(text)}`);
|
||||
node.properties!['is:raw'] = true;
|
||||
return node;
|
||||
};
|
||||
|
||||
const rootDir = new URL('../../', import.meta.url);
|
||||
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
react(),
|
||||
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',
|
||||
}),
|
||||
prefetch({
|
||||
throttle: 3,
|
||||
}),
|
||||
Unocss({
|
||||
configFile: fileURLToPath(new URL('unocss.config.ts', rootDir)),
|
||||
}),
|
||||
critters(),
|
||||
compress(),
|
||||
],
|
||||
markdown: {
|
||||
extendDefaultPlugins: true,
|
||||
syntaxHighlight: false,
|
||||
},
|
||||
vite: {
|
||||
resolve: {
|
||||
alias: {
|
||||
'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)),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
94
apps/guide/contentlayer.config.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { remarkCodeHike } from '@code-hike/mdx';
|
||||
import { defineDocumentType, makeSource } from 'contentlayer/source-files';
|
||||
import { type Node, toString } from 'hast-util-to-string';
|
||||
import { h } from 'hastscript';
|
||||
import { escape } from 'html-escaper';
|
||||
import rehypeAutolinkHeadings from 'rehype-autolink-headings';
|
||||
import rehypeSlug from 'rehype-slug';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import codeHikeThemeDarkPlus from './src/styles/code-hike-theme-dark-plus.json';
|
||||
|
||||
export const Content = defineDocumentType(() => ({
|
||||
name: 'Content',
|
||||
filePathPattern: `**/*.mdx`,
|
||||
contentType: 'mdx',
|
||||
fields: {
|
||||
title: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computedFields: {
|
||||
slug: {
|
||||
type: 'string',
|
||||
// eslint-disable-next-line unicorn/prefer-string-replace-all
|
||||
resolve: (doc) => doc._raw.flattenedPath.replace(/\d+-/g, ''),
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
// eslint-disable-next-line unicorn/prefer-string-replace-all
|
||||
resolve: (doc) => `/guide/${doc._raw.flattenedPath.replace(/\d+-/g, '')}`,
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const LinkIcon = h(
|
||||
'svg',
|
||||
{
|
||||
width: '1.25rem',
|
||||
height: '1.25rem',
|
||||
viewBox: '0 0 24 24',
|
||||
fill: 'none',
|
||||
stroke: 'currentColor',
|
||||
strokeWidth: '2',
|
||||
strokeLinecap: 'round',
|
||||
strokeLinejoin: 'round',
|
||||
},
|
||||
h('path', {
|
||||
// eslint-disable-next-line id-length
|
||||
d: 'M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71',
|
||||
}),
|
||||
h('path', {
|
||||
// eslint-disable-next-line id-length
|
||||
d: 'M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71',
|
||||
}),
|
||||
);
|
||||
|
||||
const createSROnlyLabel = (text: any) => {
|
||||
return h('span', { class: 'sr-only' }, `Section titled ${escape(text)}`);
|
||||
};
|
||||
|
||||
export default makeSource({
|
||||
contentDirPath: 'src/content',
|
||||
documentTypes: [Content],
|
||||
mdx: {
|
||||
remarkPlugins: [remarkGfm, [remarkCodeHike, { theme: codeHikeThemeDarkPlus, lineNumbers: true }]],
|
||||
rehypePlugins: [
|
||||
rehypeSlug,
|
||||
[
|
||||
rehypeAutolinkHeadings,
|
||||
{
|
||||
properties: {
|
||||
class:
|
||||
'relative inline-flex place-items-center place-content-center outline-none text-black dark:text-white pr-2 -ml-8 opacity-0 group-hover:opacity-100',
|
||||
},
|
||||
behavior: 'prepend',
|
||||
content: (heading: Node) => [
|
||||
h(
|
||||
`span.anchor-icon`,
|
||||
{
|
||||
ariaHidden: 'true',
|
||||
},
|
||||
LinkIcon,
|
||||
),
|
||||
createSROnlyLabel(toString(heading)),
|
||||
],
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
});
|
||||
5
apps/guide/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
24
apps/guide/next.config.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
// import bundleAnalyzer from '@next/bundle-analyzer';
|
||||
// import { withContentlayer } from 'next-contentlayer';
|
||||
const bundleAnalyzer = require('@next/bundle-analyzer');
|
||||
const { withContentlayer } = require('next-contentlayer');
|
||||
|
||||
const withBundleAnalyzer = bundleAnalyzer({
|
||||
enabled: process.env.ANALYZE === 'true',
|
||||
});
|
||||
|
||||
module.exports = withBundleAnalyzer(
|
||||
withContentlayer({
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
appDir: true,
|
||||
},
|
||||
images: {
|
||||
dangerouslyAllowSVG: true,
|
||||
contentDispositionType: 'attachment',
|
||||
contentSecurityPolicy: "default-src 'self'; frame-src 'none'; sandbox;",
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -5,15 +5,23 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "vitest run",
|
||||
"test:lighthouse": "lighthouse http://localhost:3000 --output-path=./lighthouse-results",
|
||||
"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=pretty",
|
||||
"format": "prettier --write . && cross-env TIMING=1 eslint src --ext .mjs,.js,.cjs,.ts,.tsx,.astro --fix --format=pretty",
|
||||
"build:prod": "yarn workspaces foreach -ptR run build && yarn build:css && yarn build:next",
|
||||
"build:next": "next build",
|
||||
"build:css": "yarn generate:css",
|
||||
"build:analyze": "cross-env-shell ANALYZE=true yarn build:prod",
|
||||
"preview": "next start",
|
||||
"dev": "concurrently 'yarn dev:contentlayer' 'yarn dev:css' 'yarn dev:next'",
|
||||
"dev:next": "next dev",
|
||||
"dev:css": "yarn generate:css --watch",
|
||||
"dev:contentlayer": "contentlayer dev",
|
||||
"generate:css": "unocss 'src/**/*.tsx' 'contentlayer.config.ts' '../../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",
|
||||
"type": "commonjs",
|
||||
"contributors": [
|
||||
"Crawl <icrawltogo@gmail.com>"
|
||||
],
|
||||
@@ -29,61 +37,68 @@
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/discordjs/discord.js.git"
|
||||
"url": "https://github.com/discordjs/discord.js.git",
|
||||
"directory": "apps/guide"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/discordjs/discord.js/issues"
|
||||
},
|
||||
"homepage": "https://discord.js.org",
|
||||
"dependencies": {
|
||||
"@code-hike/mdx": "^0.7.4",
|
||||
"@code-hike/mdx": "^0.8.2",
|
||||
"@discordjs/ui": "workspace:^",
|
||||
"ariakit": "^2.0.0-next.41",
|
||||
"@react-icons/all-files": "^4.1.0",
|
||||
"@vercel/analytics": "^1.0.0",
|
||||
"@vercel/edge-config": "^0.1.8",
|
||||
"@vercel/og": "^0.5.4",
|
||||
"ariakit": "^2.0.0-next.44",
|
||||
"cmdk": "^0.2.0",
|
||||
"contentlayer": "^0.3.2",
|
||||
"next": "^13.3.4",
|
||||
"next-contentlayer": "^0.3.2",
|
||||
"next-themes": "^0.2.1",
|
||||
"react": "^18.2.0",
|
||||
"react-custom-scrollbars-2": "^4.5.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.6.0",
|
||||
"react-use": "^17.4.0"
|
||||
"react-use": "^17.4.0",
|
||||
"rehype-autolink-headings": "^6.1.1",
|
||||
"rehype-ignore": "^1.0.5",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"rehype-slug": "^5.1.0",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"server-only": "^0.0.1",
|
||||
"sharp": "^0.32.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/image": "^0.11.6",
|
||||
"@astrojs/mdx": "^0.12.0",
|
||||
"@astrojs/prefetch": "^0.1.1",
|
||||
"@astrojs/react": "^1.2.2",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@next/bundle-analyzer": "^13.3.4",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.4.3",
|
||||
"@types/node": "16.18.3",
|
||||
"@types/react": "^18.0.25",
|
||||
"@types/react-dom": "^18.0.9",
|
||||
"@types/react-syntax-highlighter": "^15.5.5",
|
||||
"@unocss/cli": "^0.46.5",
|
||||
"@unocss/reset": "^0.46.5",
|
||||
"@vitejs/plugin-react": "^2.2.0",
|
||||
"@vitest/coverage-c8": "^0.25.3",
|
||||
"astro": "^1.6.11",
|
||||
"astro-compress": "^1.1.12",
|
||||
"astro-critters": "^1.1.12",
|
||||
"@types/html-escaper": "^3.0.0",
|
||||
"@types/node": "18.16.3",
|
||||
"@types/react": "^18.2.0",
|
||||
"@types/react-dom": "^18.2.1",
|
||||
"@unocss/cli": "^0.51.8",
|
||||
"@unocss/eslint-config": "^0.51.8",
|
||||
"@unocss/reset": "^0.51.8",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"@vitest/coverage-c8": "^0.30.1",
|
||||
"concurrently": "^8.0.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.28.0",
|
||||
"eslint-config-neon": "^0.1.40",
|
||||
"eslint-formatter-pretty": "^4.1.0",
|
||||
"happy-dom": "^7.7.0",
|
||||
"eslint": "^8.39.0",
|
||||
"eslint-config-neon": "^0.1.42",
|
||||
"eslint-formatter-pretty": "^5.0.0",
|
||||
"happy-dom": "^9.10.1",
|
||||
"hast-util-to-string": "^2.0.0",
|
||||
"hastscript": "^7.1.0",
|
||||
"hastscript": "^7.2.0",
|
||||
"html-escaper": "^3.0.3",
|
||||
"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.1.0",
|
||||
"sharp": "^0.31.2",
|
||||
"shiki": "^0.11.1",
|
||||
"typescript": "^4.9.3",
|
||||
"unocss": "^0.46.5",
|
||||
"vercel": "^28.5.6",
|
||||
"vitest": "^0.25.3"
|
||||
"lighthouse": "^10.1.1",
|
||||
"prettier": "^2.8.8",
|
||||
"typescript": "^5.0.4",
|
||||
"unocss": "^0.51.8",
|
||||
"vercel": "^29.0.3",
|
||||
"vitest": "^0.29.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.9.0"
|
||||
"node": ">=18.13.0"
|
||||
}
|
||||
}
|
||||
|
||||
BIN
apps/guide/public/assets/after-sorting.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/guide/public/assets/before-sorting.png
Normal file
|
After Width: | Height: | Size: 8.8 KiB |
BIN
apps/guide/public/assets/bot-auth-page.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
apps/guide/public/assets/bot-authorized.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
apps/guide/public/assets/bot-in-memberlist.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
apps/guide/public/assets/bot-user.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
apps/guide/public/assets/create-app.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
apps/guide/public/assets/discordjs.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
apps/guide/public/assets/old-guide.png
Executable file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
apps/guide/public/assets/snek-bot.jpeg
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
12
apps/guide/src/app/error.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
'use client';
|
||||
|
||||
export default function Error({ error }: { error: Error }) {
|
||||
console.error(error);
|
||||
|
||||
return (
|
||||
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
|
||||
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
|
||||
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
23
apps/guide/src/app/global-error.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
'use client';
|
||||
|
||||
import { Providers } from './providers';
|
||||
import { inter } from '~/util/fonts';
|
||||
|
||||
export default function GlobalError({ error }: { error: Error }) {
|
||||
console.error(error);
|
||||
|
||||
return (
|
||||
<html className={inter.variable} lang="en" suppressHydrationWarning>
|
||||
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
|
||||
<Providers>
|
||||
<main className="mx-auto max-w-2xl min-h-screen">
|
||||
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
|
||||
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">500</h1>
|
||||
<h2 className="text-[2rem] md:text-[3rem]">Error.</h2>
|
||||
</div>
|
||||
</main>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
1
apps/guide/src/app/guide/[...slug]/not-found.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from '~/app/not-found';
|
||||
21
apps/guide/src/app/guide/[...slug]/page.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { allContents } from 'contentlayer/generated';
|
||||
import { notFound } from 'next/navigation';
|
||||
import { Mdx } from '~/components/Mdx';
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return allContents.map((content) => ({ slug: [content.slug] }));
|
||||
}
|
||||
|
||||
export default function Page({ params }: { params: { slug: string[] } }) {
|
||||
const content = allContents.find((content) => content.slug === params.slug?.join('/'));
|
||||
|
||||
if (!content) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return (
|
||||
<article className="max-w-none prose">
|
||||
<Mdx code={content?.body.code ?? ''} />
|
||||
</article>
|
||||
);
|
||||
}
|
||||
25
apps/guide/src/app/guide/layout.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { Providers } from './providers';
|
||||
import Footer from '~/components/Footer';
|
||||
import Header from '~/components/Header';
|
||||
import { Nav } from '~/components/Nav';
|
||||
|
||||
export default function Layout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<Providers>
|
||||
<main className="mx-auto max-w-7xl px-4 lg:max-w-full">
|
||||
<Header />
|
||||
<div className="relative top-6 mx-auto max-w-7xl gap-6 lg:max-w-full lg:flex">
|
||||
<div className="lg:sticky lg:top-23 lg:h-[calc(100vh_-_105px)]">
|
||||
<Nav />
|
||||
</div>
|
||||
|
||||
<div className="mx-auto max-w-5xl min-w-xs w-full pb-10">
|
||||
{children}
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</Providers>
|
||||
);
|
||||
}
|
||||
3
apps/guide/src/app/guide/page.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return null;
|
||||
}
|
||||
8
apps/guide/src/app/guide/providers.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { NavProvider } from '~/contexts/nav';
|
||||
|
||||
export function Providers({ children }: PropsWithChildren) {
|
||||
return <NavProvider>{children}</NavProvider>;
|
||||
}
|
||||
86
apps/guide/src/app/layout.tsx
Normal file
@@ -0,0 +1,86 @@
|
||||
import { Analytics } from '@vercel/analytics/react';
|
||||
import type { Metadata } from 'next';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { Providers } from './providers';
|
||||
import { DESCRIPTION } from '~/util/constants';
|
||||
import { inter, jetBrainsMono } from '~/util/fonts';
|
||||
|
||||
import '@unocss/reset/tailwind-compat.css';
|
||||
import '~/styles/unocss.css';
|
||||
import '~/styles/cmdk.css';
|
||||
import '@code-hike/mdx/styles.css';
|
||||
import '~/styles/ch.css';
|
||||
import '~/styles/main.css';
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'discord.js',
|
||||
description: DESCRIPTION,
|
||||
viewport: {
|
||||
minimumScale: 1,
|
||||
initialScale: 1,
|
||||
width: 'device-width',
|
||||
},
|
||||
icons: {
|
||||
other: [
|
||||
{
|
||||
url: '/favicon-32x32.png',
|
||||
sizes: '32x32',
|
||||
type: 'image/png',
|
||||
},
|
||||
{
|
||||
url: '/favicon-16x16.png',
|
||||
sizes: '16x16',
|
||||
type: 'image/png',
|
||||
},
|
||||
],
|
||||
apple: [
|
||||
'/apple-touch-icon.png',
|
||||
{
|
||||
url: '/safari-pinned-tab.svg',
|
||||
rel: 'mask-icon',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
manifest: '/site.webmanifest',
|
||||
|
||||
themeColor: [
|
||||
{ media: '(prefers-color-scheme: light)', color: '#f1f3f5' },
|
||||
{ media: '(prefers-color-scheme: dark)', color: '#181818' },
|
||||
],
|
||||
colorScheme: 'light dark',
|
||||
|
||||
appleWebApp: {
|
||||
title: 'discord.js',
|
||||
},
|
||||
|
||||
applicationName: 'discord.js',
|
||||
|
||||
openGraph: {
|
||||
siteName: 'discord.js',
|
||||
type: 'website',
|
||||
title: 'discord.js',
|
||||
description: DESCRIPTION,
|
||||
images: 'https://discordjs.dev/api/open-graph.png',
|
||||
},
|
||||
|
||||
twitter: {
|
||||
card: 'summary_large_image',
|
||||
creator: '@iCrawlToGo',
|
||||
},
|
||||
|
||||
other: {
|
||||
'msapplication-TileColor': '#090a16',
|
||||
},
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }: PropsWithChildren) {
|
||||
return (
|
||||
<html className={`${inter.variable} ${jetBrainsMono.variable}`} lang="en" suppressHydrationWarning>
|
||||
<body className="bg-light-600 dark:bg-dark-600 dark:text-light-900">
|
||||
<Providers>{children}</Providers>
|
||||
<Analytics />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
20
apps/guide/src/app/loading.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
export default function Loading() {
|
||||
return (
|
||||
<div className="mx-4 min-h-screen flex flex-col items-center justify-center gap-4">
|
||||
<svg
|
||||
className="h-9 w-9 animate-spin text-black dark:text-white"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path
|
||||
className="opacity-75 dark:opacity-100"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<div className="text-lg font-medium">Loading...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
16
apps/guide/src/app/not-found.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="mx-auto max-w-lg min-h-screen flex flex-col place-content-center place-items-center gap-8 px-8 py-16 lg:px-6 lg:py-0">
|
||||
<h1 className="text-[9rem] font-black leading-none md:text-[12rem]">404</h1>
|
||||
<h2 className="text-[2rem] md:text-[3rem]">Not found.</h2>
|
||||
<Link
|
||||
className="h-11 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-blurple px-6 text-base font-semibold leading-none text-white no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-white"
|
||||
href="/guide"
|
||||
>
|
||||
Take me back
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
3
apps/guide/src/app/page.tsx
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function Page() {
|
||||
return null;
|
||||
}
|
||||
8
apps/guide/src/app/providers.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { ThemeProvider } from 'next-themes';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
export function Providers({ children }: PropsWithChildren) {
|
||||
return <ThemeProvider attribute="class">{children}</ThemeProvider>;
|
||||
}
|
||||
1
apps/guide/src/assets/powered-by-vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="212" height="44" fill="none"><rect width="212" height="44" fill="#000" rx="8"/><path fill="#fff" d="M60.438 15.227V26.5h1.406v-4.023h2.836c2.117 0 3.625-1.493 3.625-3.602 0-2.148-1.477-3.648-3.61-3.648h-4.257Zm1.406 1.25h2.484c1.633 0 2.531.851 2.531 2.398 0 1.492-.93 2.352-2.53 2.352h-2.485v-4.75Zm11.5 10.171c2.399 0 3.883-1.656 3.883-4.359 0-2.71-1.484-4.36-3.883-4.36-2.398 0-3.883 1.65-3.883 4.36 0 2.703 1.485 4.36 3.883 4.36Zm0-1.21c-1.594 0-2.492-1.157-2.492-3.149 0-2 .898-3.148 2.492-3.148 1.594 0 2.492 1.148 2.492 3.148 0 1.992-.898 3.148-2.492 3.148Zm15.954-7.36h-1.352l-1.656 6.735h-.125l-1.883-6.735h-1.29l-1.882 6.735h-.125l-1.656-6.735h-1.36l2.36 8.422h1.36l1.874-6.516h.125l1.883 6.516h1.367l2.36-8.422Zm4.523 1.04c1.336 0 2.227.984 2.258 2.476h-4.64c.101-1.492 1.039-2.477 2.382-2.477Zm2.219 5.202c-.352.742-1.086 1.14-2.172 1.14-1.43 0-2.36-1.054-2.43-2.718v-.062h6.055v-.516c0-2.617-1.383-4.234-3.656-4.234-2.313 0-3.797 1.718-3.797 4.367 0 2.664 1.46 4.351 3.797 4.351 1.844 0 3.156-.89 3.547-2.328H96.04Zm3.242 2.18h1.344v-5.219c0-1.187.93-2.047 2.211-2.047.266 0 .75.047.86.078V17.97a5.77 5.77 0 0 0-.672-.04c-1.117 0-2.086.579-2.336 1.4h-.125v-1.25h-1.281V26.5Zm8.899-7.383c1.336 0 2.227.985 2.258 2.477h-4.641c.102-1.492 1.04-2.477 2.383-2.477Zm2.219 5.203c-.352.742-1.086 1.14-2.172 1.14-1.43 0-2.359-1.054-2.43-2.718v-.062h6.055v-.516c0-2.617-1.383-4.234-3.656-4.234-2.313 0-3.797 1.718-3.797 4.367 0 2.664 1.461 4.351 3.797 4.351 1.844 0 3.156-.89 3.547-2.328H110.4Zm6.36 2.328c1.164 0 2.164-.554 2.695-1.492h.125V26.5h1.281V14.734h-1.343v4.672h-.118c-.476-.922-1.468-1.476-2.64-1.476-2.141 0-3.539 1.718-3.539 4.36 0 2.648 1.382 4.358 3.539 4.358Zm.312-7.507c1.524 0 2.477 1.218 2.477 3.148 0 1.945-.946 3.148-2.477 3.148-1.539 0-2.461-1.18-2.461-3.148 0-1.96.93-3.148 2.461-3.148Zm14.462 7.507c2.133 0 3.531-1.726 3.531-4.359 0-2.648-1.391-4.36-3.531-4.36-1.156 0-2.18.571-2.641 1.477h-.125v-4.672h-1.344V26.5h1.282v-1.344h.125c.531.938 1.531 1.492 2.703 1.492Zm-.313-7.507c1.539 0 2.453 1.18 2.453 3.148 0 1.969-.914 3.148-2.453 3.148-1.531 0-2.484-1.203-2.484-3.148s.953-3.148 2.484-3.148Zm6.04 10.406c1.492 0 2.164-.578 2.882-2.531l3.29-8.938h-1.43l-2.305 6.93h-.125l-2.312-6.93h-1.453l3.117 8.43-.157.5c-.351 1.015-.773 1.383-1.546 1.383-.188 0-.399-.008-.563-.04V29.5c.188.031.422.047.602.047Zm17.391-3.047 3.898-11.273h-2.148l-2.813 8.921h-.132l-2.836-8.921h-2.227l3.938 11.273h2.32Zm8.016-7.18c1.164 0 1.93.813 1.969 2.078h-4.024c.086-1.25.899-2.078 2.055-2.078Zm1.984 4.828c-.281.633-.945.985-1.906.985-1.273 0-2.094-.89-2.141-2.313v-.101h5.969v-.625c0-2.696-1.461-4.313-3.898-4.313-2.477 0-4.016 1.727-4.016 4.477s1.516 4.414 4.031 4.414c2.016 0 3.446-.969 3.797-2.524h-1.836Zm3.547 2.352h1.938v-4.938c0-1.195.875-1.976 2.133-1.976.328 0 .843.055.992.11v-1.798c-.18-.054-.524-.085-.805-.085-1.101 0-2.023.625-2.258 1.468h-.132v-1.328h-1.868V26.5Zm13.501-5.672c-.203-1.797-1.532-3.047-3.727-3.047-2.57 0-4.078 1.649-4.078 4.422 0 2.813 1.516 4.469 4.086 4.469 2.164 0 3.508-1.203 3.719-2.992h-1.844c-.203.89-.875 1.367-1.883 1.367-1.32 0-2.117-1.047-2.117-2.844 0-1.773.789-2.797 2.117-2.797 1.063 0 1.703.594 1.883 1.422h1.844Zm5.117-1.508c1.164 0 1.93.813 1.969 2.078h-4.024c.086-1.25.899-2.078 2.055-2.078Zm1.985 4.828c-.282.633-.946.985-1.907.985-1.273 0-2.093-.89-2.14-2.313v-.101h5.968v-.625c0-2.696-1.461-4.313-3.898-4.313-2.477 0-4.016 1.727-4.016 4.477s1.516 4.414 4.032 4.414c2.015 0 3.445-.969 3.796-2.524h-1.835Zm3.625 2.352h1.937V14.648h-1.937V26.5ZM23.325 13l9.325 16H14l9.325-16Z"/><path stroke="#5E5E5E" d="M43.5 0v44"/></svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
@@ -1,3 +1,79 @@
|
||||
export function DocsLink() {
|
||||
return null;
|
||||
import { FiExternalLink } from '@react-icons/all-files/fi/FiExternalLink';
|
||||
import { BASE_URL, BASE_URL_LEGACY, PACKAGES, VERSION } from '~/util/constants';
|
||||
|
||||
interface DocsLinkOptions {
|
||||
/**
|
||||
* Whether to apply brackets to the end of the symbol to denote a method.
|
||||
*
|
||||
* @remarks Functions automatically infer this.
|
||||
*/
|
||||
brackets?: boolean;
|
||||
/**
|
||||
* The package.
|
||||
*
|
||||
* @defaultValue `'discord.js'`
|
||||
*/
|
||||
package?: (typeof PACKAGES)[number];
|
||||
/**
|
||||
* The initial documentation class, function, interface etc.
|
||||
*
|
||||
* @example `'Client'`
|
||||
*/
|
||||
parent: string;
|
||||
/**
|
||||
* Whether to reference a static property.
|
||||
*
|
||||
* @remarks
|
||||
* This should only be used for the https://discord.js.org domain
|
||||
* as static properties are not identified in the URL.
|
||||
*/
|
||||
static?: boolean;
|
||||
/**
|
||||
* The symbol belonging to the parent.
|
||||
*
|
||||
* @example '`login'`
|
||||
*/
|
||||
symbol?: string;
|
||||
/**
|
||||
* The type of the {@link DocsLinkOptions.parent}.
|
||||
*
|
||||
* @example `'class'`
|
||||
* @example `'Function'`
|
||||
*/
|
||||
type: string;
|
||||
}
|
||||
|
||||
export function DocsLink({
|
||||
package: docs = PACKAGES[0],
|
||||
type,
|
||||
parent,
|
||||
symbol,
|
||||
brackets,
|
||||
static: staticReference,
|
||||
}: DocsLinkOptions) {
|
||||
const bracketText = brackets || type.toUpperCase() === 'FUNCTION' ? '()' : '';
|
||||
const trimmedSymbol = symbol;
|
||||
let url;
|
||||
let text;
|
||||
|
||||
if (docs === PACKAGES[0]) {
|
||||
url = `${BASE_URL_LEGACY}/${VERSION}/${type}/${parent}`;
|
||||
if (trimmedSymbol) url += `?scrollTo=${trimmedSymbol}`;
|
||||
|
||||
text = `${parent}${trimmedSymbol ? (trimmedSymbol.startsWith('s-') ? '.' : '#') : ''}${
|
||||
// eslint-disable-next-line prefer-named-capture-group
|
||||
trimmedSymbol ? `${trimmedSymbol.replace(/(e|s)-/, '')}` : ''
|
||||
}${bracketText}`;
|
||||
} else {
|
||||
url = `${BASE_URL}/${docs}/stable/${parent}:${type}`;
|
||||
if (trimmedSymbol) url += `#${trimmedSymbol}`;
|
||||
text = `${parent}${trimmedSymbol ? `${staticReference ? '.' : '#'}${trimmedSymbol}` : ''}${bracketText}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<a className="inline-flex flex-row place-items-center gap-1" href={url} rel="noopener noreferrer" target="_blank">
|
||||
{text}
|
||||
<FiExternalLink size={18} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { FiExternalLink } from 'react-icons/fi';
|
||||
|
||||
export function ExternalLink({ href, title }: { href: string; title: string }) {
|
||||
return (
|
||||
<a className="text-blurple inline-flex place-items-center gap-2 text-sm font-semibold" href={href}>
|
||||
<p>{title}</p>
|
||||
<FiExternalLink size={18} />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
81
apps/guide/src/components/Footer.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import Image from 'next/image';
|
||||
import vercelLogo from '~/assets/powered-by-vercel.svg';
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<footer className="md:pl-12 md:pr-12">
|
||||
<div className="mx-auto max-w-6xl flex flex-col place-items-center gap-12 pt-12 lg:place-content-center">
|
||||
<div className="w-full flex flex-col place-content-between place-items-center gap-12 md:flex-row md:gap-0">
|
||||
<a
|
||||
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://vercel.com/?utm_source=discordjs&utm_campaign=oss"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
title="Vercel"
|
||||
>
|
||||
<Image
|
||||
alt="Vercel"
|
||||
blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAABLCAQAAAA1k5H2AAAAi0lEQVR42u3SMQEAAAgDoC251a3gL2SgmfBYBRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARCAgwWEOSWBnYbKggAAAABJRU5ErkJggg=="
|
||||
height={44}
|
||||
placeholder="blur"
|
||||
src={vercelLogo}
|
||||
width={212}
|
||||
/>
|
||||
</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="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://discord.gg/djs"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Discord
|
||||
</a>
|
||||
<a
|
||||
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://github.com/discordjs/discord.js/discussions"
|
||||
rel="external 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="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord.js
|
||||
</a>
|
||||
<a
|
||||
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://discord.js.org/docs"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord.js documentation
|
||||
</a>
|
||||
<a
|
||||
className="rounded outline-none focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://discord-api-types.dev"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
discord-api-types
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H1.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
||||
|
||||
export function H1({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
|
||||
return (
|
||||
<h1 className={`group ${className}`} {...props}>
|
||||
{children}
|
||||
</h1>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H2.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
||||
|
||||
export function H2({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
|
||||
return (
|
||||
<h2 className={`group ${className}`} {...props}>
|
||||
{children}
|
||||
</h2>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H3.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
||||
|
||||
export function H3({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
|
||||
return (
|
||||
<h3 className={`group ${className}`} {...props}>
|
||||
{children}
|
||||
</h3>
|
||||
);
|
||||
}
|
||||
9
apps/guide/src/components/H4.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { HTMLAttributes, PropsWithChildren } from 'react';
|
||||
|
||||
export function H4({ children, className, ...props }: PropsWithChildren<HTMLAttributes<HTMLHeadingElement>>) {
|
||||
return (
|
||||
<h4 className={`group ${className}`} {...props}>
|
||||
{children}
|
||||
</h4>
|
||||
);
|
||||
}
|
||||
91
apps/guide/src/components/Header.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
'use client';
|
||||
|
||||
import { VscGithubInverted } from '@react-icons/all-files/vsc/VscGithubInverted';
|
||||
import { VscMenu } from '@react-icons/all-files/vsc/VscMenu';
|
||||
import { Button } from 'ariakit/button';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Fragment, useMemo } from 'react';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
|
||||
const ThemeSwitcher = dynamic(async () => import('./ThemeSwitcher'));
|
||||
|
||||
export default function Header() {
|
||||
const pathname = usePathname();
|
||||
const { setOpened } = useNav();
|
||||
|
||||
const pathElements = useMemo(
|
||||
() =>
|
||||
pathname
|
||||
.split('/')
|
||||
.slice(1)
|
||||
.map((path, idx, original) => (
|
||||
<Link
|
||||
className="rounded outline-none hover:underline focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href={`/${original.slice(0, idx + 1).join('/')}`}
|
||||
key={`${path}-${idx}`}
|
||||
>
|
||||
{path}
|
||||
</Link>
|
||||
)),
|
||||
[pathname],
|
||||
);
|
||||
|
||||
const breadcrumbs = useMemo(
|
||||
() =>
|
||||
pathElements.flatMap((el, idx, array) => {
|
||||
if (idx === 0) {
|
||||
return (
|
||||
<Fragment key={`${el.key}-${idx}`}>
|
||||
<div className="mx-2">/</div>
|
||||
<div>{el}</div>
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
if (idx !== array.length - 1) {
|
||||
return (
|
||||
<Fragment key={`${el.key}-${idx}`}>
|
||||
<div>{el}</div>
|
||||
<div className="mx-2">/</div>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return <div key={`${el.key}-${idx}`}>{el}</div>;
|
||||
}),
|
||||
[pathElements],
|
||||
);
|
||||
|
||||
return (
|
||||
<header className="sticky top-4 z-20 border border-light-900 rounded-md bg-white/75 shadow backdrop-blur-md dark:border-dark-100 dark:bg-dark-600/75">
|
||||
<div className="block h-16 px-6">
|
||||
<div className="h-full flex flex-row place-content-between place-items-center gap-8">
|
||||
<Button
|
||||
aria-label="Menu"
|
||||
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none lg:hidden active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
onClick={() => setOpened((open) => !open)}
|
||||
>
|
||||
<VscMenu size={24} />
|
||||
</Button>
|
||||
<div className="hidden lg:flex lg:grow lg:flex-row lg:overflow-hidden">{breadcrumbs}</div>
|
||||
<div className="flex flex-row place-items-center gap-4">
|
||||
<Button
|
||||
aria-label="GitHub"
|
||||
as="a"
|
||||
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
href="https://github.com/discordjs/discord.js"
|
||||
rel="external noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<VscGithubInverted size={24} />
|
||||
</Button>
|
||||
<ThemeSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
32
apps/guide/src/components/Mdx.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
'use client';
|
||||
|
||||
import { Alert, Section, DiscordMessages, DiscordMessage, DiscordMessageEmbed } from '@discordjs/ui';
|
||||
import { useMDXComponent } from 'next-contentlayer/hooks';
|
||||
import { H1 } from './H1';
|
||||
import { H2 } from './H2';
|
||||
import { H3 } from './H3';
|
||||
import { H4 } from './H4';
|
||||
import { DocsLink } from '~/components/DocsLink';
|
||||
import { ResultingCode } from '~/components/ResultingCode';
|
||||
|
||||
export function Mdx({ code }: { code: string }) {
|
||||
const Component = useMDXComponent(code);
|
||||
|
||||
return (
|
||||
<Component
|
||||
components={{
|
||||
Alert,
|
||||
Section,
|
||||
DiscordMessages,
|
||||
DiscordMessage,
|
||||
DiscordMessageEmbed,
|
||||
DocsLink,
|
||||
ResultingCode,
|
||||
h1: H1,
|
||||
h2: H2,
|
||||
h3: H3,
|
||||
h4: H4,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
30
apps/guide/src/components/Nav.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
'use client';
|
||||
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { Sidebar } from './Sidebar';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
|
||||
export function Nav() {
|
||||
const { opened } = useNav();
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={`dark:bg-dark-600/75 dark:border-dark-100 border-light-900 top-22 fixed bottom-4 left-4 right-4 z-20 mx-auto max-w-5xl rounded-md border bg-white/75 shadow backdrop-blur-md ${
|
||||
opened ? 'block' : 'hidden'
|
||||
} lg:min-w-xs lg:sticky lg:block lg:h-full lg:w-full lg:max-w-xs`}
|
||||
>
|
||||
<Scrollbars
|
||||
autoHide
|
||||
className="[&>div]:overscroll-none"
|
||||
hideTracksWhenNotNeeded
|
||||
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
|
||||
renderTrackVertical={(props) => (
|
||||
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
|
||||
)}
|
||||
universal
|
||||
>
|
||||
<Sidebar />
|
||||
</Scrollbars>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
import { Button } from 'ariakit/button';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { FiCommand } from 'react-icons/fi';
|
||||
import { VscColorMode, VscGithubInverted, VscMenu, VscSearch } from 'react-icons/vsc';
|
||||
import { useMedia } from 'react-use';
|
||||
import { Sidebar } from './Sidebar.jsx';
|
||||
import type { MDXPage } from './SidebarItems.jsx';
|
||||
|
||||
export function Navbar({ pages }: { pages?: MDXPage[] | undefined }) {
|
||||
const matches = useMedia('(min-width: 992px)', false);
|
||||
const [opened, setOpened] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (matches) {
|
||||
setOpened(false);
|
||||
}
|
||||
}, [matches]);
|
||||
|
||||
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">Placeholder</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>
|
||||
<Sidebar opened={opened} pages={pages} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +1,19 @@
|
||||
import type { MarkdownHeading } from 'astro';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import { VscListSelection } from 'react-icons/vsc';
|
||||
import { useLocation } from 'react-use';
|
||||
|
||||
const LINK_HEIGHT = 30;
|
||||
const INDICATOR_SIZE = 10;
|
||||
const INDICATOR_OFFSET = (LINK_HEIGHT - INDICATOR_SIZE) / 2;
|
||||
|
||||
export function Outline({ headings }: { headings: MarkdownHeading[] }) {
|
||||
const state = useLocation();
|
||||
const [active, setActive] = useState(0);
|
||||
export function Outline({ headings }: { headings: any[] }) {
|
||||
// eslint-disable-next-line react/hook-use-state
|
||||
const [active /* setActive */] = useState(0);
|
||||
|
||||
const headingItems = useMemo(
|
||||
() =>
|
||||
headings.map((heading, idx) => (
|
||||
<a
|
||||
className={`dark:border-dark-100 border-light-800 pl-6.5 focus:ring-width-2 focus:ring-blurple ml-[10px] border-l p-[5px] text-sm outline-0 focus:rounded focus:border-0 focus:ring ${
|
||||
className={`dark:border-dark-100 border-light-800 pl-6.5 focus:ring-width-2 focus:ring-blurple ml-[10px] border-l p-[5px] text-sm outline-none focus:rounded focus:border-0 focus:ring ${
|
||||
idx === active
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
@@ -32,32 +29,32 @@ export function Outline({ headings }: { headings: MarkdownHeading[] }) {
|
||||
[headings, active],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const idx = headings.findIndex((heading) => heading.slug === state.hash?.slice(1));
|
||||
if (idx >= 0) {
|
||||
setActive(idx);
|
||||
}
|
||||
}, [state, headings]);
|
||||
// useEffect(() => {
|
||||
// const idx = headings.findIndex((heading) => heading.slug === state.hash?.slice(1));
|
||||
// if (idx >= 0) {
|
||||
// setActive(idx);
|
||||
// }
|
||||
// }, [state, headings]);
|
||||
|
||||
return (
|
||||
<Scrollbars
|
||||
autoHide
|
||||
hideTracksWhenNotNeeded
|
||||
renderThumbVertical={(props) => <div {...props} className="dark:bg-dark-100 bg-light-900 z-30 rounded" />}
|
||||
renderThumbVertical={(props) => <div {...props} className="z-30 rounded bg-light-900 dark:bg-dark-100" />}
|
||||
renderTrackVertical={(props) => (
|
||||
<div {...props} className="absolute top-0.5 right-0.5 bottom-0.5 z-30 w-1.5 rounded" />
|
||||
<div {...props} className="absolute bottom-0.5 right-0.5 top-0.5 z-30 w-1.5 rounded" />
|
||||
)}
|
||||
universal
|
||||
>
|
||||
<div className="flex flex-col break-all p-3 pb-8">
|
||||
<div className="mt-4 ml-2 flex flex-row gap-2">
|
||||
<VscListSelection size={25} />
|
||||
<div className="ml-2 mt-4 flex flex-row gap-2">
|
||||
{/* <VscListSelection size={25} /> */}
|
||||
<span className="font-semibold">Contents</span>
|
||||
</div>
|
||||
<div className="mt-4 ml-2 flex flex-col gap-2">
|
||||
<div className="ml-2 mt-4 flex flex-col gap-2">
|
||||
<div className="relative flex flex-col">
|
||||
<div
|
||||
className="bg-blurple absolute h-[10px] w-[10px] rounded-full border-2 border-black dark:border-white"
|
||||
className="absolute h-[10px] w-[10px] border-2 border-black rounded-full bg-blurple dark:border-white"
|
||||
style={{
|
||||
left: INDICATOR_SIZE / 2 + 0.5,
|
||||
transform: `translateY(${active * LINK_HEIGHT + INDICATOR_OFFSET}px)`,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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"
|
||||
className="flex flex-row flex-col transform-gpu cursor-pointer select-none appearance-none place-items-center gap-2 rounded bg-light-600 px-4 py-3 leading-none no-underline outline-none active:translate-y-px active:bg-light-800 dark:bg-dark-600 hover:bg-light-700 focus:ring focus:ring-width-2 focus:ring-blurple dark:active:bg-dark-400 dark:hover:bg-dark-500"
|
||||
href={url}
|
||||
>
|
||||
<h3 className="text-md font-semibold">{title}</h3>
|
||||
|
||||
8
apps/guide/src/components/Section.tsx
Normal file
@@ -0,0 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { Section as DJSSection, type SectionOptions } from '@discordjs/ui';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
export function Section(options: PropsWithChildren<SectionOptions>) {
|
||||
return <DJSSection {...options} />;
|
||||
}
|
||||
@@ -1,24 +1,62 @@
|
||||
import { Scrollbars } from 'react-custom-scrollbars-2';
|
||||
import type { MDXPage } from './SidebarItems.jsx';
|
||||
'use client';
|
||||
|
||||
import { allContents } from 'contentlayer/generated';
|
||||
import Link from 'next/link';
|
||||
import { usePathname } from 'next/navigation';
|
||||
import { Section } from './Section';
|
||||
import { useNav } from '~/contexts/nav';
|
||||
|
||||
const items = allContents.map((content) => ({
|
||||
title: content.title,
|
||||
category: content.category,
|
||||
slug: content.slug,
|
||||
href: content.url,
|
||||
}));
|
||||
|
||||
function transformItemsByCategory(allContents: typeof items) {
|
||||
return allContents.reduce<Record<string, typeof items>>((accumulator: any, content) => {
|
||||
if (!accumulator[content.category]) {
|
||||
accumulator[content.category] = [];
|
||||
}
|
||||
|
||||
accumulator[content.category].push(content);
|
||||
return accumulator;
|
||||
}, {});
|
||||
}
|
||||
|
||||
const itemsByCategory = transformItemsByCategory(items);
|
||||
|
||||
export function Sidebar() {
|
||||
const pathname = usePathname();
|
||||
const { setOpened } = useNav();
|
||||
|
||||
export function Sidebar({ pages, opened }: { opened: boolean; pages?: MDXPage[] | undefined }) {
|
||||
return (
|
||||
<nav
|
||||
className={`h-[calc(100vh - 73px)] dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[73px] left-0 bottom-0 z-20 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
|
||||
>
|
||||
{pages ?? null}
|
||||
</Scrollbars>
|
||||
</nav>
|
||||
<div className="flex flex-col gap-3 p-3">
|
||||
{Object.keys(itemsByCategory).map((category, idx) => (
|
||||
<Section
|
||||
buttonClassName="bg-light-600 hover:bg-light-700 active:bg-light-800 dark:bg-dark-400 dark:hover:bg-dark-300 dark:active:bg-dark-400 focus:ring-width-2 focus:ring-blurple rounded p-3 outline-none focus:ring"
|
||||
key={`${category}-${idx}`}
|
||||
title={category}
|
||||
>
|
||||
{itemsByCategory[category]?.map((member, index) => (
|
||||
<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-none focus:rounded focus:border-0 focus:ring ${
|
||||
decodeURIComponent(pathname ?? '') === member.href
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
}`}
|
||||
href={member.href}
|
||||
key={`${member.title}-${index}`}
|
||||
onClick={() => setOpened(false)}
|
||||
title={member.title}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
|
||||
<span className="truncate">{member.title}</span>
|
||||
</div>
|
||||
</Link>
|
||||
))}
|
||||
</Section>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import { Section } from '@discordjs/ui';
|
||||
import type { MDXInstance } from 'astro';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useLocation } from 'react-use';
|
||||
|
||||
export type MDXPage = MDXInstance<{ category: string; title: string }>;
|
||||
|
||||
export function SidebarItems({ pages }: { pages: MDXPage[] }) {
|
||||
const state = useLocation();
|
||||
const [active, setActive] = useState<string | undefined>('');
|
||||
|
||||
const categories = useMemo(
|
||||
() =>
|
||||
pages.reduce<Record<string, MDXPage[]>>((acc, page) => {
|
||||
if (acc[page.frontmatter.category]) {
|
||||
acc[page.frontmatter.category]?.push(page);
|
||||
} else {
|
||||
acc[page.frontmatter.category] = [page];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {}),
|
||||
[pages],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setActive(state.pathname);
|
||||
}, [state]);
|
||||
|
||||
return Object.keys(categories).map((category, idx) => (
|
||||
<Section key={idx} title={category}>
|
||||
{categories[category]?.map((member, index) => (
|
||||
<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 ${
|
||||
(member.url || '/') === active
|
||||
? 'bg-blurple text-white'
|
||||
: 'dark:hover:bg-dark-200 dark:active:bg-dark-100 hover:bg-light-700 active:bg-light-800'
|
||||
}`}
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
href={member.url || '/'}
|
||||
key={index}
|
||||
title={member.frontmatter.title}
|
||||
>
|
||||
<div className="flex flex-row place-items-center gap-2 lg:text-sm">
|
||||
<span className="truncate">{member.frontmatter.title}</span>
|
||||
</div>
|
||||
</a>
|
||||
)) ?? null}
|
||||
</Section>
|
||||
));
|
||||
}
|
||||
@@ -1,180 +0,0 @@
|
||||
---
|
||||
import { Separator } from 'ariakit/separator';
|
||||
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, 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>
|
||||
window.addEventListener('load', () => {
|
||||
const headings = document.querySelectorAll(
|
||||
'div.level-h1 > h1, div.level-h2 > h2, div.level-h3 > h3, div.level-h4 > h4',
|
||||
);
|
||||
|
||||
const headingsObserver = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const location = window.location.toString().split('#')[0];
|
||||
history.replaceState(null, '', location + '#' + entry.target.id);
|
||||
}
|
||||
});
|
||||
},
|
||||
{
|
||||
root: null,
|
||||
rootMargin: '-100px 0% -66%',
|
||||
threshold: 1.0,
|
||||
},
|
||||
);
|
||||
|
||||
headings.forEach((heading) => headingsObserver.observe(heading));
|
||||
});
|
||||
</script>
|
||||
|
||||
<Navbar client:load>
|
||||
<div class="flex flex-col gap-3 p-3 pb-32 lg:pb-12" slot="pages">
|
||||
<SidebarItems client:load pages={pages} />
|
||||
</div>
|
||||
</Navbar>
|
||||
<main class="pt-18 lg:pl-76 xl:pr-64">
|
||||
<article class="dark:bg-dark-600 bg-light-600">
|
||||
<div class="dark:bg-dark-800 relative z-10 min-h-[calc(100vh_-_70px)] bg-white p-6 pb-20 shadow">
|
||||
<div class="prose max-w-full">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="h-[calc(100vh - 72px)] dark:bg-dark-600 dark:border-dark-100 border-light-800 fixed top-[72px] right-0 bottom-0 z-20 hidden w-64 border-l bg-white pr-2 xl:block"
|
||||
>
|
||||
<Outline client:load headings={headings} />
|
||||
</div>
|
||||
<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 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">
|
||||
<a
|
||||
class="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"
|
||||
>
|
||||
<img alt="Vercel" src="/powered-by-vercel.svg" />
|
||||
</a>
|
||||
<div class="flex flex-row gap-6 md:gap-12">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div class="text-lg font-semibold">Community</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<a
|
||||
class="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
|
||||
class="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 class="flex flex-col gap-2">
|
||||
<div class="text-lg font-semibold">Project</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<a
|
||||
class="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
|
||||
class="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
|
||||
class="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>
|
||||
<div>Test</div>
|
||||
</main>
|
||||
20
apps/guide/src/components/ThemeSwitcher.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client';
|
||||
|
||||
import { VscColorMode } from '@react-icons/all-files/vsc/VscColorMode';
|
||||
import { Button } from 'ariakit/button';
|
||||
import { useTheme } from 'next-themes';
|
||||
|
||||
export default function ThemeSwitcher() {
|
||||
const { resolvedTheme, setTheme } = useTheme();
|
||||
const toggleTheme = () => setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
|
||||
|
||||
return (
|
||||
<Button
|
||||
aria-label="Toggle theme"
|
||||
className="h-6 w-6 flex flex-row transform-gpu cursor-pointer select-none appearance-none place-items-center border-0 rounded rounded-full bg-transparent p-0 text-sm font-semibold leading-none no-underline outline-none active:translate-y-px focus:ring focus:ring-width-2 focus:ring-blurple"
|
||||
onClick={() => toggleTheme()}
|
||||
>
|
||||
<VscColorMode size={24} />
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
layout: '../layouts/SidebarLayout.astro'
|
||||
title: Introduction
|
||||
category: Home
|
||||
---
|
||||
@@ -1,26 +1,21 @@
|
||||
---
|
||||
layout: '../layouts/SidebarLayout.astro'
|
||||
title: What's new
|
||||
category: Home
|
||||
---
|
||||
|
||||
import { CH } from '@code-hike/mdx/components';
|
||||
import { DiscordMessages, DiscordMessage } from '@discordjs/ui';
|
||||
|
||||
# What's new
|
||||
|
||||
<DiscordMessages rounded>
|
||||
<DiscordMessage
|
||||
interaction={{
|
||||
author: {
|
||||
avatar:
|
||||
'https://cdn.discordapp.com/guilds/222078108977594368/users/81440962496172032/avatars/c059c5d04d717ea05790f7a6447e4843.webp?size=160',
|
||||
username: 'Crawl',
|
||||
avatar: '/assets/old-guide.png',
|
||||
username: 'discord.js',
|
||||
},
|
||||
command: 'upgrade',
|
||||
command: '/upgrade',
|
||||
}}
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/474807795183648809/7f239a0776ff928b2182906a2b3743c9.webp?size=160',
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
username: 'Guide Bot',
|
||||
time: 'Today at 21:00',
|
||||
@@ -34,14 +29,11 @@ import { DiscordMessages, DiscordMessage } from '@discordjs/ui';
|
||||
|
||||
## Site
|
||||
|
||||
- Upgraded to [VuePress v2](https://v2.vuepress.vuejs.org/)
|
||||
- New theme made to match the [discord.js documentation site](https://discord.js.org/)
|
||||
- Discord message components upgraded to [@discord-message-components/vue](https://github.com/Danktuary/discord-message-components/blob/main/packages/vue/README.md)
|
||||
- Many fixes in code blocks, grammar, consistency, etc.
|
||||
We have moved from VuePress to [Next.js](https://nextjs.org/)! The source can be found [here](https://github.com/discordjs/discord.js/tree/main/apps/guide).
|
||||
|
||||
## Pages
|
||||
|
||||
All content has been updated to use discord.js v14 syntax. The v13 version of the guide can be found at [https://v13.discordjs.guide/](https://v13.discordjs.guide/).
|
||||
All content has been updated to use discord.js v14 syntax. The v13 version of the guide can be found at https://v13.discordjs.guide.
|
||||
|
||||
### New
|
||||
|
||||
@@ -58,13 +50,13 @@ All content has been updated to use discord.js v14 syntax. The v13 version of th
|
||||
- [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')`_
|
||||
- [Message content will become a new privileged intent on August 31, 2022](https://support-dev.discord.com/hc/en-us/articles/4404772028055)
|
||||
- _`client.on('message')`_ snippets updated to _`client.on(Events.InteractionCreate)`_
|
||||
- [Message content became a privileged intent on August 31, 2022](https://support-dev.discord.com/hc/articles/4404772028055)
|
||||
|
||||
<DiscordMessages rounded>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: 'https://cdn.discordapp.com/avatars/474807795183648809/7f239a0776ff928b2182906a2b3743c9.webp?size=160',
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
username: 'Guide Bot',
|
||||
time: 'Today at 21:00',
|
||||
198
apps/guide/src/content/01-home/03-how-to-contribute.mdx
Normal file
@@ -0,0 +1,198 @@
|
||||
---
|
||||
title: How to contribute
|
||||
category: Home
|
||||
---
|
||||
|
||||
# How to contribute
|
||||
|
||||
Since this guide is made specifically for the discord.js community, we want to be sure to provide the most relevant and up-to-date content. We will, of course, make additions to the current pages and add new ones as we see fit, but fulfilling requests is how we know we're providing content you all want the most.
|
||||
|
||||
Requests may be as simple as "add an example to the [frequently asked questions](/popular-topics/faq.html) page", or as elaborate as "add a page regarding [sharding](/sharding/)". We'll do our best to fulfill all requests, as long as they're reasonable.
|
||||
|
||||
To make a request, simply head over to [the repository's issue tracker](https://github.com/discordjs/discord.js/issues) and [create a new issue](https://github.com/discordjs/discord.js/issues/new)! Title it appropriately, and let us know exactly what you mean inside the issue description. Make sure that you've looked around the site before making a request; what you want to request might already exist!
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
Remember that you can always [fork the repository](https://github.com/discordjs/discord.js/fork) and [make a pull
|
||||
request](https://github.com/discordjs/discord.js/pulls) if you want to add anything to the guide yourself!
|
||||
</Alert>
|
||||
|
||||
We'll also get into some of the more advanced features this guide does below.
|
||||
|
||||
## Components
|
||||
|
||||
Throughout the guide, you'll see some components from the _`@discordjs/ui`_ package:
|
||||
|
||||
- _`Alert`_
|
||||
- _`Section`_
|
||||
- _`DiscordMessages`_, _`DiscordMessage`_, and _`DiscordMessageEmbed`_
|
||||
|
||||
Check the source of this page to see them in action!
|
||||
|
||||
### Alert
|
||||
|
||||
This component may take a _`title`_ and a _`type`_ of _`'danger' | 'info' | 'success' | 'warning'`_.
|
||||
|
||||
This uses _`title="Alert" type="info"`_:
|
||||
|
||||
<Alert title="Alert" type="info">
|
||||
Use these appropriately!
|
||||
</Alert>
|
||||
|
||||
### Section
|
||||
|
||||
<Section title="Expand me!" padding defaultClosed background gutter>
|
||||
Well, hello there!
|
||||
|
||||
Whenever some text does not need to be in the main body, you can put it here.
|
||||
|
||||
- _`title`_: The title that'll appear.
|
||||
- _`padding`_: Adds padding.
|
||||
- _`dense`_: When _`padding`_ is specified, _`dense`_ could make it appear, well, dense.
|
||||
- _`defaultClosed`_ Whether the section is closed by default. This one was.
|
||||
- _`background`_ Adds background to the content.
|
||||
- _`gutter`_: This adds a very small appealing space between the expansion of the section and its content.
|
||||
|
||||
</Section>
|
||||
|
||||
### DiscordMessages, DiscordMessage, and DiscordMessageEmbed
|
||||
|
||||
<DiscordMessages>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
time: 'Today at 21:00',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
>
|
||||
A _`DiscordMessage`_ must be within _`DiscordMessages`_.
|
||||
</DiscordMessage>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
time: 'Today at 21:01',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
reply={{
|
||||
author: {
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
username: 'Guide Bot',
|
||||
},
|
||||
content: 'A _`DiscordMessage`_ must be within _`DiscordMessages`_.',
|
||||
}}
|
||||
time="21:02"
|
||||
>
|
||||
It's much better to see the source code of this page to replicate and learn!
|
||||
</DiscordMessage>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
time: 'Today at 21:02',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
>
|
||||
This message depicts the use of embeds.
|
||||
<>
|
||||
<DiscordMessageEmbed
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
footer={{ content: 'Sometimes, titles just have to be.' }}
|
||||
title={{ title: 'An amazing title' }}
|
||||
>
|
||||
This is a description. You can put a description here. It must be descriptive!
|
||||
</DiscordMessageEmbed>
|
||||
<DiscordMessageEmbed
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
footer={{ content: "When one amazing title just wasn't enough." }}
|
||||
title={{ title: 'Another amazing title' }}
|
||||
>
|
||||
Multiple embeds!
|
||||
</DiscordMessageEmbed>
|
||||
</>
|
||||
</DiscordMessage>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
time: 'Today at 21:03',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
interaction={{
|
||||
author: {
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
username: 'Guide Bot',
|
||||
},
|
||||
command: '/interaction',
|
||||
}}
|
||||
>
|
||||
Interactions are supported! I definitely used a command.
|
||||
</DiscordMessage>
|
||||
<DiscordMessage
|
||||
author={{
|
||||
avatar: '/assets/discordjs.png',
|
||||
bot: true,
|
||||
color: 'text-red-500',
|
||||
time: 'Today at 21:04',
|
||||
username: 'Guide Bot',
|
||||
}}
|
||||
reply={{
|
||||
author: {
|
||||
avatar: '/assets/snek-bot.jpeg',
|
||||
bot: true,
|
||||
verified: true,
|
||||
color: 'text-blue-500',
|
||||
username: 'Snek Bot',
|
||||
},
|
||||
content: 'You can also have verified bots, like me!',
|
||||
}}
|
||||
>
|
||||
Display colors are supported as well!
|
||||
</DiscordMessage>
|
||||
</DiscordMessages>
|
||||
|
||||
## Code blocks
|
||||
|
||||
We use [Code Hike](https://codehike.org). Here are some example code blocks, which should be easy to grasp and learn upon reading the source code of this page:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```ts
|
||||
const HELLO = 'hello' as const;
|
||||
console.log(HELLO);
|
||||
// "ts" is the language of the code block.
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```ts fileName
|
||||
const FILE_NAME = 'fileName' as const;
|
||||
if (FILE_NAME.includes(' ')) throw new Error('Spaces cannot be used in file names.');
|
||||
```
|
||||
|
||||
```ts anotherFileName
|
||||
const FILE_NAME_2 = 'anotherFileName' as const;
|
||||
// Putting code blocks together makes them appear in tabs, just like in your editor.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
```ts requiredName
|
||||
const FILE_NAME_3 = 'requiredName' as const;
|
||||
if (!FILE_NAME) throw new Error('There must be a file name to use panels!');
|
||||
// The --- divider was used to create a panel.
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
For more information, be sure to check out the [documentation](https://codehike.org/docs/ch-code).
|
||||
@@ -0,0 +1,102 @@
|
||||
---
|
||||
title: Installing Node.js and discord.js
|
||||
category: Installations and preparations
|
||||
---
|
||||
|
||||
# Installing Node.js and discord.js
|
||||
|
||||
## Installing Node.js
|
||||
|
||||
To use discord.js, you'll need to install [Node.js](https://nodejs.org/). discord.js v14 requires Node v16.9.0 or higher.
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
To check if you already have Node installed on your machine \(e.g., if you're using a VPS\), run _`node -v`_ in your
|
||||
terminal. If it outputs _`v16.9.0`_ or higher, then you're good to go! Otherwise, continue reading.
|
||||
</Alert>
|
||||
|
||||
On Windows, it's as simple as installing any other program. Download the latest version from [the Node.js website](https://nodejs.org/), open the downloaded file, and follow the steps from the installer.
|
||||
|
||||
On macOS, either:
|
||||
|
||||
- Download the latest version from [the Node.js website](https://nodejs.org/), open the package installer, and follow the instructions
|
||||
- Use a package manager like [Homebrew](https://brew.sh/) with the command _`brew install node`_
|
||||
|
||||
On Linux, you can consult [this page](https://nodejs.org/en/download/package-manager/) to determine how you should install Node. Native package managers often default to outdated versions of Node, so make sure you follow the recommended approach for your chosen Linux distribution carefully.
|
||||
|
||||
## Preparing the essentials
|
||||
|
||||
To use discord.js, you'll need to install it via npm \(Node's package manager\). npm comes with every Node installation, so you don't have to worry about installing that. However, before you install anything, you should set up a new project folder.
|
||||
|
||||
Navigate to a suitable place on your machine and create a new folder named _`discord-bot`_ (or whatever you want). Next you'll need to open your terminal.
|
||||
|
||||
### Opening the terminal
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
If you use [Visual Studio Code](https://code.visualstudio.com/), you can press <kbd>Ctrl + `</kbd> (backtick) to open
|
||||
its integrated terminal.
|
||||
</Alert>
|
||||
|
||||
On Windows, either:
|
||||
|
||||
- <kbd>Shift + Right-click</kbd> inside your project directory and choose the "Open command window here" option
|
||||
- Press <kbd>Win + R</kbd> and run _`cmd.exe`_, and then _`cd`_ into your project directory
|
||||
|
||||
On macOS, either:
|
||||
|
||||
- Open Launchpad or Spotlight and search for "Terminal"
|
||||
- In your "Applications" folder, under "Utilities", open the Terminal app
|
||||
|
||||
On Linux, you can quickly open the terminal with <kbd>Ctrl + Alt + T</kbd>.
|
||||
|
||||
With the terminal open, run the _`node -v`_ command to make sure you've successfully installed Node.js. If it outputs _`v16.9.0`_ or higher, great!
|
||||
|
||||
### Initiating a project folder
|
||||
|
||||
<CH.Code lineNumbers={false}>
|
||||
|
||||
```sh npm
|
||||
npm init; npm pkg set type="module"
|
||||
```
|
||||
|
||||
```sh yarn
|
||||
yarn init
|
||||
# You must go into your package.json file and add "type": "module"
|
||||
```
|
||||
|
||||
```sh pnpm
|
||||
pnpm init; pnpm pkg set type="module"
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
This is the next command you'll be running. This command creates a _`package.json`_ file for you, which will keep track of the dependencies your project uses, as well as other info.
|
||||
|
||||
This command will ask you a sequence of questions–you should fill them out as you see fit. If you're not sure of something or want to skip it as a whole, leave it blank and press enter. Setting the package type as _`module`_ tells Node that you'll be writing this project using ESM \(ECMAScript modules\), supporting the latest JavaScript syntax and features.
|
||||
|
||||
Once you're done with that, you're ready to install discord.js!
|
||||
|
||||
## Installing discord.js
|
||||
|
||||
Now that you've installed Node.js and know how to open your console and run commands, you can finally install discord.js! Run the following command in your terminal:
|
||||
|
||||
<CH.Code lineNumbers={false}>
|
||||
|
||||
```sh npm
|
||||
npm install discord.js
|
||||
```
|
||||
|
||||
```sh yarn
|
||||
yarn add discord.js
|
||||
```
|
||||
|
||||
```sh pnpm
|
||||
pnpm add discord.js
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
And that's it! With all the necessities installed, you're almost ready to start coding your bot.
|
||||
|
||||
## Installing a linter
|
||||
|
||||
While you are coding, it's possible to run into numerous syntax errors or code in an inconsistent style. You should [install a linter](./setting-up-a-linter) to ease these troubles. While code editors generally can point out syntax errors, linters coerce your code into a specific style as defined by the configuration. While this is not required, it is advised.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Setting up a linter
|
||||
category: Installations and preparations
|
||||
---
|
||||
|
||||
TODO: Rewrite. Placeholder page for ordering.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
title: Setting up a bot application
|
||||
category: Installations and preparations
|
||||
---
|
||||
|
||||
# Setting up a bot application
|
||||
|
||||
## Creating your bot
|
||||
|
||||
Now that you've installed Node, discord.js, and hopefully a linter, you're almost ready to start coding! The next step you need to take is setting up an actual Discord bot application via Discord's website.
|
||||
|
||||
It's effortless to create one. The steps you need to take are as follows:
|
||||
|
||||
1. Open the [Discord developer portal](https://discord.com/developers/applications) and log into your account.
|
||||
2. Click on the "New Application" button.
|
||||
3. Enter a name and confirm the pop-up window by clicking the "Create" button.
|
||||
|
||||
You should see a page like this:
|
||||
|
||||

|
||||
|
||||
You can edit your application's name, description, and avatar here. Once you've saved your changes, move on by selecting the "Bot" tab in the left pane.
|
||||
|
||||
## Your bot's token
|
||||
|
||||
<Alert title="Important" type="danger">
|
||||
This section is critical, so pay close attention. It explains what your bot token is, as well as the security aspects
|
||||
of it.
|
||||
</Alert>
|
||||
|
||||
On the bot tab, you'll see a section like this:
|
||||
|
||||

|
||||
|
||||
In this panel, you can give your bot a snazzy avatar, set its username, and make it public or private. Your bot's token will be revealed when you press the "Reset Token" button and confirm. When we ask you to paste your bot's token somewhere, this is the value that you need to put in. If you happen to lose your bot's token at some point, you need to come back to this page and reset your bot's token again which will reveal the new token, invalidating all old ones.
|
||||
|
||||
### What is a token, anyway?
|
||||
|
||||
A token is essentially your bot's password; it's what your bot uses to login to Discord. With that said, **it is vital that you do not ever share this token with anybody, purposely or accidentally**. If someone does manage to get a hold of your bot's token, they can use your bot as if it were theirs—this means they can perform malicious acts with it.
|
||||
|
||||
Tokens look like this: _`NzkyNzE1NDU0MTk2MDg4ODQy.X-hvzA.Ovy4MCQywSkoMRRclStW4xAYK7I`_ (don't worry, we immediately reset this token before even posting it here!). If it's any shorter and looks more like this: _`kxbsDRU5UfAaiO7ar9GFMHSlmTwYaIYn`_, you copied your client secret instead. Make sure to copy the token if you want your bot to work!
|
||||
|
||||
### Token leak scenario
|
||||
|
||||
Let's imagine that you have a bot on over 1,000 servers, and it took you many, many months of coding and patience to get it on that amount. Your bot's token gets leaked somewhere, and now someone else has it. That person can:
|
||||
|
||||
- Spam every server your bot is on;
|
||||
- DM spam as many users as possible;
|
||||
- Delete as many channels as possible;
|
||||
- Kick or ban as many server members as possible;
|
||||
- Make your bot leave all of the servers it has joined;
|
||||
|
||||
All that and much, much more. Sounds pretty terrible, right? So make sure to keep your bot's token as safe as possible!
|
||||
|
||||
In the [configuration files](../creating-your-bot/configuration-files) page of the guide, we cover how to safely store your bot's token in a configuration file.
|
||||
|
||||
<Alert title="Compromised tokens" type="danger">
|
||||
If your bot token has been compromised by committing it to a public repository, posting it in discord.js support etc.
|
||||
or otherwise see your bot's token in danger, return to this page and press "Reset Token". This will invalidate all old
|
||||
tokens belonging to your bot. Keep in mind that you will need to update your bot's token where you used it before.
|
||||
</Alert>
|
||||
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Adding your bot to servers
|
||||
category: Installations and preparations
|
||||
---
|
||||
|
||||
# Adding your bot to servers
|
||||
|
||||
After you [set up a bot application](./setting-up-a-bot-application), you'll notice that it's not in any servers yet. So how does that work?
|
||||
|
||||
Before you're able to see your bot in your own (or other) servers, you'll need to add it by creating and using a unique invite link using your bot application's client id.
|
||||
|
||||
## Bot invite links
|
||||
|
||||
The basic version of one such link looks like this:
|
||||
|
||||
<CH.Code lineNumbers={false}>
|
||||
|
||||
```
|
||||
https://discord.com/api/oauth2/authorize?client_id=123456789012345678&permissions=0&scope=bot%20applications.commands
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The structure of the URL is quite simple:
|
||||
|
||||
- _`https://discord.com/api/oauth2/authorize`_ is Discord's standard structure for authorizing an OAuth2 application (such as your bot application) for entry to a Discord server.
|
||||
- _`client_id=...`_ is to specify _which_ application you want to authorize. You'll need to replace this part with your client's id to create a valid invite link.
|
||||
- _`permissions=...`_ describes the permissions that your bot will request to be granted by default upon joining the server you are adding it to.
|
||||
- _`scope=bot%20applications.commands`_ specifies that you want to add this application as a Discord bot, with the ability to create slash commands.
|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
If you get an error message saying "Bot requires a code grant", head over to your application's settings and disable
|
||||
the "Require OAuth2 Code Grant" option. You shouldn't enable this option unless you know why you need to.
|
||||
</Alert>
|
||||
|
||||
## Creating and using your invite link
|
||||
|
||||
To create an invite link, head back to the [My Apps](https://discord.com/developers/applications/me) page under the "Applications" section, click on your bot application, and open the OAuth2 page.
|
||||
|
||||
In the sidebar, you'll find the OAuth2 URL generator. Select the _`bot`_ and _`applications.commands`_ options. Once you select the _`bot`_ option, a list of permissions will appear, allowing you to configure the permissions your bot needs.
|
||||
|
||||
Grab the link via the "Copy" button and enter it in your browser. You should see something like this (with your bot's username and avatar):
|
||||
|
||||

|
||||
|
||||
Choose the server you want to add it to and click "Authorize". Do note that you'll need the "Manage Server" permission on a server to add your bot there. This should then present you a nice confirmation message:
|
||||
|
||||

|
||||
|
||||
Congratulations! You've successfully added your bot to your Discord server. It should show up in your server's member list somewhat like this:
|
||||
|
||||

|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
title: Configuration files
|
||||
category: Creating your bot
|
||||
---
|
||||
|
||||
# Configuration files
|
||||
|
||||
Once you [add your bot to a server](../installations-and-preparations/adding-your-bot-to-servers), the next step is to start coding and get it online! Let's start by creating a config file to prepare the necessary values your client will need.
|
||||
|
||||
As explained in the ["What is a token, anyway?"](../installations-and-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
|
||||
|
||||
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 importing this file.
|
||||
|
||||
<CH.Code lineNumbers={false}>
|
||||
|
||||
```json config.json
|
||||
{
|
||||
"token": "your-token-goes-here"
|
||||
}
|
||||
```
|
||||
|
||||
```js index.js
|
||||
import config from './config.json' assert { type: 'json' };
|
||||
|
||||
console.log(config.token);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Danger" type="danger">
|
||||
If you're using Git, you should not commit this file and should [ignore it via `.gitignore`](#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.
|
||||
|
||||
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 lineNumbers={false} rows={3}>
|
||||
|
||||
```sh Shell
|
||||
A=123 B=456 DISCORD_TOKEN=your-token-goes-here node index.js
|
||||
```
|
||||
|
||||
```js index.js
|
||||
console.log(process.env.A);
|
||||
console.log(process.env.B);
|
||||
console.log(process.env.DISCORD_TOKEN);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### 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.
|
||||
|
||||
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 lineNumbers={false}>
|
||||
|
||||
```sh npm
|
||||
npm install dotenv
|
||||
```
|
||||
|
||||
```sh yarn
|
||||
yarn add dotenv
|
||||
```
|
||||
|
||||
```sh pnpm
|
||||
pnpm add dotenv
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<CH.Code lineNumbers={false} rows={7}>
|
||||
|
||||
```sh .env
|
||||
A=123
|
||||
B=456
|
||||
DISCORD_TOKEN=your-token-goes-here
|
||||
```
|
||||
|
||||
```js index.js
|
||||
import { config } from 'dotenv';
|
||||
|
||||
config();
|
||||
|
||||
console.log(process.env.A);
|
||||
console.log(process.env.B);
|
||||
console.log(process.env.DISCORD_TOKEN);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Danger" type="danger">
|
||||
If you're using Git, you should not commit this file and should [ignore it via `.gitignore`](#git-and-gitignore).
|
||||
</Alert>
|
||||
|
||||
<Alert title="Online editors (Glitch, Heroku, Replit, etc.)" type="info">
|
||||
While we generally do not recommend using online editors as hosting solutions, but rather invest in a proper virtual private server, these services do offer ways to keep your credentials safe as well! Please see the respective service's documentation and help articles for more information on how to keep sensitive values safe:
|
||||
|
||||
- Glitch: [Storing secrets in .env](https://glitch.happyfox.com/kb/article/18)
|
||||
- Heroku: [Configuration variables](https://devcenter.heroku.com/articles/config-vars)
|
||||
- Replit: [Secrets and environment variables](https://docs.replit.com/repls/secrets-environment-variables)
|
||||
|
||||
</Alert>
|
||||
|
||||
## 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:
|
||||
|
||||
```
|
||||
node_modules
|
||||
.env
|
||||
config.json
|
||||
```
|
||||
|
||||
<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!
|
||||
|
||||
</Alert>
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: Creating the main file
|
||||
category: Creating your bot
|
||||
---
|
||||
|
||||
# Creating the main file
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
This page assumes you've already prepared the [configuration files](./configuration-files) from the previous page.
|
||||
We're using the _`config.json`_ approach, however feel free to substitute your own!
|
||||
</Alert>
|
||||
|
||||
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:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
// Require the necessary discord.js classes
|
||||
import { Client, Events, GatewayIntentBits } from 'discord.js';
|
||||
import config from './config.json' assert { type: 'json' };
|
||||
|
||||
// Create a new client instance
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
// When the client is ready, run this code (only once)
|
||||
// We use 'c' for the event parameter to keep it separate from the already defined 'client'
|
||||
client.once(Events.ClientReady, (c) => {
|
||||
console.log(`Ready! Logged in as ${c.user.tag}`);
|
||||
});
|
||||
|
||||
// Log in to Discord with your client's token
|
||||
client.login(config.token);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
This is how you create a client instance for your Discord bot and log in to Discord. The _`GatewayIntentBits.Guilds`_ intents option is necessary for the discord.js client to work as you expect it to, as it ensures that the caches for guilds, channels, and roles are populated and available for internal use.
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
The term "guild" is used by the Discord API and in discord.js to refer to a Discord server.
|
||||
</Alert>
|
||||
|
||||
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).
|
||||
|
||||
## Running your application
|
||||
|
||||
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! The next step is to start adding [slash commands](./adding-commands) to develop your bot's functionality.
|
||||
|
||||
<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 process is a quick way to start it up again.
|
||||
|
||||
</Alert>
|
||||
|
||||
#### Resulting code
|
||||
|
||||
<ResultingCode path="creating-your-bot/initial-files" />
|
||||
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: Adding commands
|
||||
category: Creating your bot
|
||||
---
|
||||
|
||||
# Creating slash commands
|
||||
|
||||
Discord allows developers to register [slash commands](https://discord.com/developers/docs/interactions/application-commands), which provide users a first-class way of interacting directly with your application.
|
||||
|
||||
Slash commands provide a huge number of benefits over manual message parsing, including:
|
||||
|
||||
- Integration with the Discord client interface.
|
||||
- Automatic command detection and parsing of the associated options/arguments.
|
||||
- Typed argument inputs for command options, e.g. "String", "User", or "Role".
|
||||
- Validated or dynamic choices for command options.
|
||||
- In-channel private responses (ephemeral messages).
|
||||
- Pop-up form-style inputs for capturing additional information.
|
||||
|
||||
...and many more!
|
||||
|
||||
<Alert title="Read first!" type="info">
|
||||
For fully functional slash commands, there are three important pieces of code that need to be written. They are:
|
||||
|
||||
1. The individual command files, containing their definitions and functionality.
|
||||
2. The [command handler](command-handling.html), which dynamically reads the files and executes the commands.
|
||||
3. The [command deployment script](command-deployment.html), to register your slash commands with Discord so they appear in the interface.
|
||||
|
||||
These steps can be done in any order, but **all are required** before the commands are fully functional.
|
||||
|
||||
On this page, you'll complete Step 1. Make sure to also complete the other pages linked above!
|
||||
|
||||
</Alert>
|
||||
|
||||
## Before you continue
|
||||
|
||||
Assuming you've followed the guide so far, your project directory should look something like this:
|
||||
|
||||
```:no-line-numbers
|
||||
discord-bot/
|
||||
├── node_modules
|
||||
├── config.json
|
||||
├── index.js
|
||||
├── package-lock.json
|
||||
└── package.json
|
||||
```
|
||||
|
||||
## Individual command files
|
||||
|
||||
Create a new folder named _`commands`_, which is where you'll store all of your command files.
|
||||
|
||||
At a minimum, the definition of a slash command must have a name and a description. Slash command names must be between 1-32 characters and contain no capital letters, spaces, or symbols other than _`-`_ and _`_`_. Using the builder, a simple _`ping`\_ command definition would look like this:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
export const data = {
|
||||
name: 'ping',
|
||||
description: 'Replies with Pong!',
|
||||
};
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
A slash command also requires a function to run when the command is used, to respond to the interaction. Using an interaction response method confirms to Discord that your bot successfully received the interaction, and has responded to the user. Discord enforces this to ensure that all slash commands provide a good user experience (UX). Failing to respond will cause Discord to show that the command failed, even if your bot is performing other actions as a result.
|
||||
|
||||
The simplest way to acknowledge and respond to an interaction is the _`interaction.reply()`_ method. Other methods of replying are covered on the [Response methods](../slash-commands/response-methods) page later in this section.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
export async function execute(interaction) {
|
||||
await interaction.reply('Pong!');
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Put these two together by creating a `commands/ping.js` file for your first command. Inside this file, you're going to define and export two items.
|
||||
|
||||
- The `data` property, which will provide the command definition shown above for registering to Discord.
|
||||
- The `execute` method, which will contain the functionality to run from our event handler when the command is used.
|
||||
|
||||
The _`export`_ keyword ensures these values can be imported and read by other files; namely the command loader and command deployment scripts mentioned earlier.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js commands/ping.js
|
||||
export const data = {
|
||||
name: 'ping',
|
||||
description: 'Replies with Pong!',
|
||||
};
|
||||
|
||||
export async function execute(interaction) {
|
||||
await interaction.reply('Pong!');
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
[`module.exports`](https://nodejs.org/api/modules.html#modules_module_exports) is how you export data in Node.js so that you can [`require()`](https://nodejs.org/api/modules.html#modules_require_id) it in other files.
|
||||
|
||||
If you need to access your client instance from inside a command file, you can access it via `interaction.client`. If you need to access external files, packages, etc., you should `require()` them at the top of the file.
|
||||
|
||||
</Alert>
|
||||
|
||||
That's it for your basic ping command. Below are examples of two more commands we're going to build upon throughout the guide, so create two more files for these before you continue reading.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js commands/user.js
|
||||
export const data = {
|
||||
name: 'user',
|
||||
description: 'Provides information about the user.',
|
||||
};
|
||||
|
||||
export async function execute(interaction) {
|
||||
// interaction.user is the object representing the User who ran the command
|
||||
// interaction.member is the GuildMember object, which represents the user in the specific guild
|
||||
await interaction.reply(
|
||||
`This command was run by ${interaction.user.username}, who joined on ${interaction.member.joinedAt}.`,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
```js commands/server.js
|
||||
export const data = {
|
||||
name: 'server',
|
||||
description: 'Provides information about the server.',
|
||||
};
|
||||
|
||||
export async function execute(interaction) {
|
||||
// interaction.guild is the object representing the Guild in which the command was run
|
||||
await interaction.reply(`This server is ${interaction.guild.name} and has
|
||||
${interaction.guild.memberCount} members.`);
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
#### Next steps
|
||||
|
||||
You can implement additional commands by creating additional files in the _`commands`_ folder, but these three are the ones we're going to use for the examples as we go on. For now let's move on to the code you'll need for command handling, to load the files and respond to incoming interactions.
|
||||
|
||||
#### Resulting code
|
||||
|
||||
<ResultingCode />
|
||||
@@ -0,0 +1,297 @@
|
||||
---
|
||||
title: Handling command interactions
|
||||
category: Creating your bot
|
||||
---
|
||||
|
||||
# Command handling
|
||||
|
||||
Unless your bot project is small, it's not a very good idea to have a single file with a giant _`if`_/_`else if`_ chain for commands. If you want to implement features into your bot and make your development process a lot less painful, you'll want to implement a command handler. Let's get started on that!
|
||||
|
||||
<Alert title="Read first!" type="info">
|
||||
For fully functional slash commands, there are three important pieces of code that need to be written. They are:
|
||||
|
||||
1. The [individual command files](slash-commands), containing their definitions and functionality.
|
||||
2. The command handler, which dynamically reads the files and executes the commands.
|
||||
3. The [command deployment script](command-deployment), to register your slash commands with Discord so they appear in the interface.
|
||||
|
||||
These steps can be done in any order, but **all are required** before the commands are fully functional.
|
||||
|
||||
This page details how to complete **Step 2**. Make sure to also complete the other pages linked above!
|
||||
|
||||
</Alert>
|
||||
|
||||
## Loading command files
|
||||
|
||||
Now that your command files have been created, your bot needs to load these files on startup.
|
||||
|
||||
In your _`index.js`_ file, make these additions to the base template:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js JavaScript mark=1:4,9
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Client, Collection, Events, GatewayIntentBits } from 'discord.js';
|
||||
import config from './config.json' assert { type: 'json' };
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
const commands = new Collection();
|
||||
|
||||
client.once(Events.ClientReady, () => {
|
||||
console.log('Ready!');
|
||||
});
|
||||
```
|
||||
|
||||
```ts TypeScript mark=1:11,16:21
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import {
|
||||
Client,
|
||||
Collection,
|
||||
Events,
|
||||
GatewayIntentBits,
|
||||
type RESTPostAPIChatInputApplicationCommandsJSONBody,
|
||||
type ChatInputCommandInteraction,
|
||||
} from 'discord.js';
|
||||
import config from './config.json';
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
interface CommandModule {
|
||||
data: RESTPostAPIChatInputApplicationCommandsJSONBody;
|
||||
execute(interaction: ChatInputCommandInteraction): Promise<void>;
|
||||
}
|
||||
|
||||
const commands = new Collection<string, CommandModule>();
|
||||
|
||||
client.once(Events.ClientReady, () => {
|
||||
console.log('Ready!');
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
<Alert title="Tip" type="info">
|
||||
- The [`fs`](https://nodejs.org/api/fs.html) module is Node's native file system module. _`readdir`_ is used to read
|
||||
the _`commands`_ directory and identify our command files. - The [`path`](https://nodejs.org/api/path.html) module is
|
||||
Node's native path utility module. _`join`_ helps construct paths to access files and directories. One of the
|
||||
advantages of _`path.join`_ is that it automatically detects the operating system and uses the appropriate joiners. -
|
||||
The [`url`](https://nodejs.org/api/url.html) module provides utilities for URL resolution and parsing.
|
||||
_`fileURLToPath`_ ensuring a cross-platform valid absolute path string.
|
||||
- The{' '}
|
||||
<DocsLink type="class" parent="Collection" /> class extends JavaScript's native
|
||||
[_`Map`_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) class, and includes
|
||||
more extensive, useful functionality. _`Collection`_ is used to store and efficiently retrieve commands for execution.
|
||||
|
||||
</Alert>
|
||||
|
||||
Next, using the modules imported above, dynamically retrieve your command files with a few more additions to the _`index.js`_ file:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js JavaScript focus=3:15
|
||||
const commands = new Collection();
|
||||
|
||||
const commandsPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```ts TypeScript focus=3:15
|
||||
const commands = new Collection<string, CommandModule>();
|
||||
|
||||
const commandsPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
First, [url.fileURLToPath()](https://nodejs.org/api/url.html) helps to construct a path to the _`commands`_ directory. The [fs.readdir()](https://nodejs.org/api/fs.html#fspromisesreaddirpath-options) method then reads the path to the directory and returns a Promise which resolves to an array of all the file names it contains, currently _`['ping.js', 'server.js', 'user.js']`_. To ensure only command files get processed, _`Array.filter()`_ removes any non-JavaScript files from the array.
|
||||
|
||||
With the correct files identified, the last step is to loop over the array and dynamically set each command into the _`commands`_ Collection. For each file being loaded, check that it has at least the _`data`_ and _`execute`_ properties. This helps to prevent errors resulting from loading empty, unfinished or otherwise incorrect command files while you're still developing.
|
||||
|
||||
## Receiving command interactions
|
||||
|
||||
Every slash command is an _`interaction`_, so to respond to a command, you need to create a listener for the <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> event that will execute code when your application receives an interaction. Place the code below in the _`index.js`_ file you created earlier.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
client.on(Events.InteractionCreate, (interaction) => {
|
||||
console.log(interaction);
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Not every interaction is a slash command (e.g. _`MessageComponent`_ interactions). Make sure to only handle slash commands in this function by making use of the <DocsLink type="class" parent="BaseInteraction" symbol="isChatInputCommand" brackets /> method to exit the handler if another type is encountered. This method also provides type guarding for TypeScript users, narrowing the type from _`BaseInteraction`_ to <DocsLink type="class" parent="ChatInputCommandInteraction" />.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=2
|
||||
client.on(Events.InteractionCreate, (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
console.log(interaction);
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Executing commands
|
||||
|
||||
When your bot receives a <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> event, the interaction object contains all the information you need to dynamically retrieve and execute your commands!
|
||||
|
||||
Let's take a look at the _`ping`_ command again. Note the _`execute()`_ function that will reply to the interaction with "Pong!".
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js
|
||||
export const data = {
|
||||
name: 'ping',
|
||||
description: 'Replies with Pong!',
|
||||
};
|
||||
|
||||
export async function execute(interaction) {
|
||||
await interaction.reply('Pong!');
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
First, you need to get the matching command from the _`commands`_ Collection based on the _`interaction.commandName`_. If no matching command is found, log an error to the console and ignore the event.
|
||||
|
||||
With the right command identified, all that's left to do is call the command's _`.execute()`_ method and pass in the _`interaction`_ variable as its argument. Note that the event listener has been made _`async`_, allowing Promises to be awaited. In case something goes wrong and the Promise rejects, catch and log any error to the console.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=4:20
|
||||
// focus[37:42]
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const command = commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
if (interaction.replied || interaction.deferred) {
|
||||
await interaction.followUp({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||
} else {
|
||||
await interaction.reply({ content: 'There was an error while executing this command!', ephemeral: true });
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
## Command categories
|
||||
|
||||
So far, all of your command files are in a single _`commands`_ folder. This is fine at first, but as your project grows, the number of files in the _`commands`_ folder will too. Keeping track of that many files can be a little tough. To make this a little easier, you can categorize your commands and put them in subfolders inside the _`commands`_ folder. You will have to make a few changes to your existing code in _`index.js`_ for this to work out.
|
||||
|
||||
If you've been following along, your project structure should look something like this:
|
||||
|
||||

|
||||
|
||||
After moving your commands into subfolders, it will look something like this:
|
||||
|
||||

|
||||
|
||||
<Alert title="Warning" type="warning">
|
||||
Make sure you put every command file you have inside one of the new subfolders. Leaving a command file directly under
|
||||
the _`commands`_ folder will create problems.
|
||||
</Alert>
|
||||
|
||||
It is not necessary to name your subfolders exactly like we have named them here. You can create any number of subfolders and name them whatever you want. Although, it is a good practice to name them according to the type of commands stored inside them.
|
||||
|
||||
Back in your _`index.js`_ file, where the code to [dynamically read command files](#loading-command-files) is, use the same pattern to read the subfolder directories, and then require each command inside them.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js JavaScript focus=3:7,19
|
||||
const commands = new Collection();
|
||||
|
||||
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFolders = await readdir(foldersPath);
|
||||
|
||||
for (const folder of commandFolders) {
|
||||
const commandsPath = join(foldersPath, folder);
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```ts Typescript mark=3:7,19
|
||||
const commands = new Collection<string, CommandModule>();
|
||||
|
||||
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFolders = await readdir(foldersPath);
|
||||
|
||||
for (const folder of commandFolders) {
|
||||
const commandsPath = join(foldersPath, folder);
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
That's it! When creating new files for commands, make sure you create them inside one of the subfolders (or a new one) in the _`commands`_ folder.
|
||||
|
||||
#### Next steps
|
||||
|
||||
Your command files are now loaded into your bot, and the event listener is prepared and ready to respond. In the next section, we cover the final step - a command deployment script you'll need to register your commands so they appear in the Discord client.
|
||||
|
||||
#### Resulting code
|
||||
|
||||
<ResultingCode />
|
||||
|
||||
It also includes some bonus commands!
|
||||
@@ -0,0 +1,155 @@
|
||||
---
|
||||
title: Registering slash commands
|
||||
category: Creating your bot
|
||||
---
|
||||
|
||||
# Registering slash commands
|
||||
|
||||
<Alert title="Read first!" type="info">
|
||||
For fully functional slash commands, you need three important pieces of code:
|
||||
|
||||
1. The [individual command files](slash-commands), containing their definitions and functionality.
|
||||
2. The [command handler](command-handling), which dynamically reads the files and executes the commands.
|
||||
3. The command deployment script, to register your slash commands with Discord so they appear in the interface.
|
||||
|
||||
These steps can be done in any order, but **all are required** before the commands are fully functional.
|
||||
|
||||
This page details how to complete **Step 3**. Make sure to also complete the other pages linked above!
|
||||
|
||||
</Alert>
|
||||
|
||||
## Command registration
|
||||
|
||||
Slash commands can be registered in two ways; in one specific guild, or for every guild the bot is in. We're going to look at single-guild registration first, as this is a good way to develop and test your commands before a global deployment.
|
||||
|
||||
Your application will need the _`applications.commands`_ scope authorized in a guild for any of its slash commands to appear, and to be able to register them in a specific guild without error.
|
||||
|
||||
Slash commands only need to be registered once, and updated when the definition (description, options etc) is changed. As there is a daily limit on command creations, it's not necessary nor desirable to connect a whole client to the gateway or do this on every _`ClientReady`_ event. As such, a standalone script using the lighter REST manager is preferred.
|
||||
|
||||
This script is intended to be run separately, only when you need to make changes to your slash command **definitions** - you're free to modify parts such as the execute function as much as you like without redeployment.
|
||||
|
||||
### Guild commands
|
||||
|
||||
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.
|
||||
|
||||
Add two more properties to your _`config.json`_ file, which we'll need in the deployment script:
|
||||
|
||||
- _`clientId`_: Your application's client id ([Discord Developer Portal](https://discord.com/developers/applications) > "General Information" > application id)
|
||||
- _`guildId`_: Your development server's id ([Enable developer mode](https://support.discord.com/hc/en-us/articles/206346498) > Right-click the server title > "Copy Server ID")
|
||||
|
||||
<CH.Code lineNumbers={false}>
|
||||
|
||||
```json
|
||||
{
|
||||
"token": "your-token-goes-here",
|
||||
"clientId": "your-application-id-goes-here",
|
||||
"guildId": "your-server-id-goes-here"
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
With these defined, you can use the deployment script below:
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js deploy-commands.js
|
||||
import { REST, Routes } from 'discord.js';
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import config from './config.json' assert { type: 'json' };
|
||||
|
||||
const { clientId, guildId, token } = config;
|
||||
|
||||
const commands = [];
|
||||
|
||||
// Grab all the command files from the commands directory you created earlier
|
||||
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFolders = await readdir(foldersPath);
|
||||
|
||||
for (const folder of commandFolders) {
|
||||
// Grab all the command files from the commands directory you created earlier
|
||||
const commandsPath = join(foldersPath, folder);
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
// Grab the SlashCommandBuilder#toJSON() output of each command's data for deployment
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.push(command.data.toJSON());
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct and prepare an instance of the REST module
|
||||
const rest = new REST().setToken(token);
|
||||
|
||||
try {
|
||||
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
const data = await rest.put(Routes.applicationGuildCommands(clientId, guildId), { body: commands });
|
||||
|
||||
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Once you fill in these values, run _`node deploy-commands.js`_ in your project directory to register your commands to the guild specified. If you see the success message, check for the commands in the server by typing _`/`_! If all goes well, you should be able to run them and see your bot's response in Discord!
|
||||
|
||||
### Global commands
|
||||
|
||||
Global application commands will be available in all the guilds your application has the _`applications.commands`_ scope authorized in, and in direct messages by default.
|
||||
|
||||
To deploy global commands, you can use the same script from the [guild commands](#guild-commands) section and simply adjust the route in the script to _`.applicationCommands(clientId)`_
|
||||
|
||||
Test
|
||||
|
||||
<CH.Code rows="focus">
|
||||
|
||||
```js focus=5
|
||||
try {
|
||||
console.log(`Started refreshing ${commands.length} application (/) commands.`);
|
||||
|
||||
// The put method is used to fully refresh all commands in the guild with the current set
|
||||
const data = await rest.put(Routes.applicationCommands(clientId), { body: commands });
|
||||
|
||||
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
|
||||
} catch (error) {
|
||||
// And of course, make sure you catch and log any errors!
|
||||
console.error(error);
|
||||
}
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
### Where to deploy
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
Guild-based deployment of commands is best suited for development and testing in your own personal server. Once you're satisfied that it's ready, deploy the command globally to publish it to all guilds that your bot is in.
|
||||
|
||||
You may wish to have a separate application and token in the Discord Dev Portal for your dev application, to avoid duplication between your guild-based commands and the global deployment.
|
||||
|
||||
</Alert>
|
||||
|
||||
#### Further reading
|
||||
|
||||
You've successfully sent a response to a slash command! However, this is only the most basic of command event and response functionality. Much more is available to enhance the user experience including:
|
||||
|
||||
- applying this same dynamic, modular handling approach to events with an [Event handler](./event-handling).
|
||||
- utilising the different [Response methods](../slash-commands/response-methods) that can be used for slash commands.
|
||||
- expanding on these examples with additional validated option types in [Advanced command creation](../slash-commands/advanced-creation).
|
||||
- adding formatted [Embeds](../popular-topics/embeds) to your responses.
|
||||
- enhancing the command functionality with [Buttons](../interactions/buttons) and [Select Menus](../interactions/select-menus).
|
||||
- prompting the user for more information with [Modals](../interactions/modals).
|
||||
|
||||
#### Resulting code
|
||||
|
||||
<ResultingCode path="creating-your-bot/command-deployment" />
|
||||
@@ -0,0 +1,219 @@
|
||||
---
|
||||
title: Event handling
|
||||
category: Creating your bot
|
||||
---
|
||||
|
||||
# Event handling
|
||||
|
||||
Node.js uses an event-driven architecture, making it possible to execute code when a specific event occurs. The discord.js library takes full advantage of this. You can visit the <DocsLink type="class" parent="Client" /> documentation to see the full list of events.
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
This page assumes you've followed the guide up to this point, and created your _`index.js`_ and individual slash
|
||||
commands according to those pages.
|
||||
</Alert>
|
||||
|
||||
At this point, your `index.js` file has code for loading commands, and listeners for two events: `ClientReady` and `InteractionCreate`.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js Commands
|
||||
const commands = new Collection();
|
||||
|
||||
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFolders = await readdir(foldersPath);
|
||||
|
||||
for (const folder of commandFolders) {
|
||||
const commandsPath = join(foldersPath, folder);
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js ClientReady
|
||||
client.once(Events.ClientReady, (c) => {
|
||||
console.log(`Ready! Logged in as ${c.user.tag}`);
|
||||
});
|
||||
```
|
||||
|
||||
```js InteractionCreate
|
||||
client.on(Events.InteractionCreate, async (interaction) => {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const command = commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(`Error executing ${interaction.commandName}`);
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
Currently, all of this code is in the _`index.js`_ file. <DocsLink type="class" parent="Client" symbol="e-ready" /> emits once when the _`Client`_ becomes ready for use, and <DocsLink type="class" parent="Client" symbol="e-interactionCreate" /> emits whenever an interaction is received.
|
||||
Moving the event listener code into individual files is simple, and we'll be taking a similar approach to the [command handler](./handling-command-interactions).
|
||||
|
||||
## Individual event files
|
||||
|
||||
Your project directory should look something like this:
|
||||
|
||||
```
|
||||
discord-bot/
|
||||
├── commands/
|
||||
├── node_modules/
|
||||
├── config.json
|
||||
├── deploy-commands.js
|
||||
├── index.js
|
||||
├── package-lock.json
|
||||
└── package.json
|
||||
```
|
||||
|
||||
Create an _`events`_ folder in the same directory. You can then move the code from your event listeners in _`index.js`_ to separate files: _`events/ready.js`_ and _`events/interactionCreate.js`_. The _`InteractionCreate`_ event is responsible for command handling, so the command loading code will move here too.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js events/interactionCreate.js
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Collection, Events } from 'discord.js';
|
||||
|
||||
const commands = new Collection();
|
||||
|
||||
const foldersPath = fileURLToPath(new URL('commands', import.meta.url));
|
||||
const commandFolders = await readdir(foldersPath);
|
||||
|
||||
for (const folder of commandFolders) {
|
||||
const commandsPath = join(foldersPath, folder);
|
||||
const commandFiles = await readdir(commandsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
for (const file of commandFiles) {
|
||||
const filePath = join(commandsPath, file);
|
||||
const command = await import(filePath);
|
||||
// Set a new item in the Collection with the key as the command name and the value as the exported module
|
||||
if ('data' in command && 'execute' in command) {
|
||||
commands.set(command.data.name, command);
|
||||
} else {
|
||||
console.log(`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const data = {
|
||||
name: Events.InteractionCreate,
|
||||
};
|
||||
|
||||
export async function execute(interaction) {
|
||||
if (!interaction.isChatInputCommand()) return;
|
||||
|
||||
const command = commands.get(interaction.commandName);
|
||||
|
||||
if (!command) {
|
||||
console.error(`No command matching ${interaction.commandName} was found.`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await command.execute(interaction);
|
||||
} catch (error) {
|
||||
console.error(`Error executing ${interaction.commandName}`);
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```js events/ready.js
|
||||
import { Events } from 'discord.js';
|
||||
|
||||
export const data = {
|
||||
name: Events.ClientReady,
|
||||
once = true,
|
||||
};
|
||||
export async function execute(client) {
|
||||
console.log(`Ready! Logged in as ${client.user.tag}`);
|
||||
}
|
||||
```
|
||||
|
||||
```js index.js
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import config from './config.json' assert { type: 'json' };
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
client.login(config.token);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
The _`name`_ property states which event this file is for, and the _`once`_ property holds a boolean value that specifies if the event should run only once. You don't need to specify this in _`interactionCreate.js`_ as the default behavior will be to run on every event instance. The _`execute`_ function holds your event logic, which will be called by the event handler whenever the event emits.
|
||||
|
||||
## Reading event files
|
||||
|
||||
Next, let's write the code for dynamically retrieving all the event files in the _`events`_ folder. We'll be taking a similar approach to our [command handler](./handling-command-interactions). Place the new code highlighted below in your _`index.js`_.
|
||||
|
||||
_`fs.readdir()`_ combined with _`array.filter()`_ returns an array of all the file names in the given directory and filters for only _`.js`_ files, i.e. _`['ready.js', 'interactionCreate.js']`_.
|
||||
|
||||
<CH.Code>
|
||||
|
||||
```js focus=9:20
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { Client, GatewayIntentBits } from 'discord.js';
|
||||
import config from './config.json' assert { type: 'json' };
|
||||
|
||||
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
|
||||
|
||||
const eventsPath = fileURLToPath(new URL('events', import.meta.url));
|
||||
const eventFiles = await readdir(eventsPath).then((files) => files.filter((file) => file.endsWith('.js')));
|
||||
|
||||
for (const file of eventFiles) {
|
||||
const filePath = join(eventsPath, file);
|
||||
const event = await import(filePath);
|
||||
if (event.data.once) {
|
||||
client.once(event.data.name, (...args) => event.execute(...args));
|
||||
} else {
|
||||
client.on(event.data.name, (...args) => event.execute(...args));
|
||||
}
|
||||
}
|
||||
|
||||
client.login(config.token);
|
||||
```
|
||||
|
||||
</CH.Code>
|
||||
|
||||
You'll notice the code looks very similar to the command loading above it - read the files in the events folder and load each one individually.
|
||||
|
||||
The <DocsLink type="class" parent="Client" /> class in discord.js extends the [`EventEmitter`](https://nodejs.org/api/events.html#events_class_eventemitter) class. Therefore, the _`client`_ object exposes the [`.on()`](https://nodejs.org/api/events.html#events_emitter_on_eventname_listener) and [`.once()`](https://nodejs.org/api/events.html#events_emitter_once_eventname_listener) methods that you can use to register event listeners. These methods take two arguments: the event name and a callback function. These are defined in your separate event files as _`name`_ and _`execute`_.
|
||||
|
||||
The callback function passed takes argument(s) returned by its respective event, collects them in an _`args`_ array using the _`...`_ [rest parameter syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters), then calls _`event.execute()`_ while passing in the _`args`_ array using the _`...`_ [spread syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax). They are used here because different events in discord.js have different numbers of arguments. The rest parameter collects these variable number of arguments into a single array, and the spread syntax then takes these elements and passes them to the _`execute`_ function.
|
||||
|
||||
After this, listening for other events is as easy as creating a new file in the _`events`_ folder. The event handler will automatically retrieve and register it whenever you restart your bot.
|
||||
|
||||
<Alert title="Tip" type="success">
|
||||
In most cases, you can access your _`client`_ instance in other files by obtaining it from one of the other discord.js
|
||||
structures, e.g. _`interaction.client`_ in the _`InteractionCreate`_ event. You do not need to manually pass it to
|
||||
your events.
|
||||
</Alert>
|
||||
|
||||
## Resulting code
|
||||
|
||||
<ResultingCode />
|
||||