Skip to content

Commit

Permalink
v3
Browse files Browse the repository at this point in the history
  • Loading branch information
twlite committed Jan 16, 2022
1 parent fa51221 commit a47873e
Show file tree
Hide file tree
Showing 14 changed files with 304 additions and 33 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
out/
node_modules/
yarn.lock
test/
yarn.lock
53 changes: 48 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,30 @@ Create Discord.js components in JSX.

# Setup

## Install discord.js master
## Install discord.js

```sh
$ npm i discord.js@dev
$ npm i discord.js
```

## Add these in your **`tsconfig.json`#compilerOptions**
# Env Setup

## TypeScript
### Add these in your **`tsconfig.json`#compilerOptions**

```json
"jsxFactory": "DiscordComponents.createComponent",
"jsx": "react",
"jsxFragmentFactory": "DiscordComponents.fragment"
"jsxFragmentFactory": "DiscordComponents.Fragment"
```

## Babel

Specify pragma for custom jsx factory with **[`@babel/plugin-transform-react-jsx`](https://babeljs.io/docs/en/babel-plugin-transform-react-jsx)**.

```jsx
/** @jsx DiscordComponents.createComponent */
/** @jsxFrag DiscordComponents.Fragment */
```

# Components Available
Expand Down Expand Up @@ -169,4 +181,35 @@ client.on("messageCreate", (message) => {
```

### Preview
![](https://i.imgur.com/eaqub2x.png)
![](https://i.imgur.com/eaqub2x.png)

## Other examples

```tsx
const componentData = (
<>
<MessageActionRow>
{Array.from({ length: 5 }, (_, i) => (
<MessageButton style="PRIMARY" label={`Button ${++i}`} customId={`btn_${i}`} />
))}
</MessageActionRow>
<MessageActionRow>
<MessageSelectMenu customId="123">
{Array.from({ length: 5 }, (_, i) => (
<MessageSelectOption description={`Option number ${++i}`} label={`Option ${i}`} value={i.toString()} />
))}
</MessageSelectMenu>
</MessageActionRow>
<MessageEmbed color="BLURPLE" title="Counter">
<MessageEmbedFields>
{Array.from({ length: 10 }, (_, i) => {
const counter = ++i;
return <MessageEmbedField name={`Count ${counter}`} value={`Counting ${counter}`} />
})}
</MessageEmbedFields>
</MessageEmbed>
</>
);

return message.channel.send(componentData);
```
27 changes: 21 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
{
"name": "discord.tsx",
"version": "2.0.0",
"version": "3.0.0",
"description": "Create Discord.js components in JSX",
"main": "out/index.js",
"types": "out/index.d.ts",
"module": "out/index.mjs",
"scripts": {
"build": "tsup",
"postbuild": "node scripts/postbuild.mjs",
"test": "yarn test:build && node test/babel.js && node test/typescript.js",
"test:build": "yarn test:babel && yarn test:ts",
"test:babel": "babel --plugins @babel/plugin-transform-react-jsx --presets @babel/preset-env test/babel.jsx -d test",
"test:ts": "tsc test/typescript.tsx --jsx react --jsxFactory DiscordComponents.createComponent --jsxFragmentFactory DiscordComponents.Fragment --outDir test"
},
"files": [
"out"
],
"repository": {
"type": "git",
"url": "git+https://github.com/DevSnowflake/discord.tsx.git"
"url": "git+https://github.com/DevAndromeda/discord.tsx.git"
},
"keywords": [
"tsx-discord.js",
Expand All @@ -20,16 +29,22 @@
"react",
"tsx"
],
"author": "Snowflake107",
"author": "DevAndromeda",
"license": "MIT",
"bugs": {
"url": "https://github.com/DevSnowflake/discord.tsx/issues"
"url": "https://github.com/DevAndromeda/discord.tsx/issues"
},
"homepage": "https://github.com/DevSnowflake/discord.tsx#readme",
"homepage": "https://github.com/DevAndromeda/discord.tsx#readme",
"devDependencies": {
"@babel/cli": "^7.16.8",
"@babel/core": "^7.16.7",
"@babel/plugin-transform-react-jsx": "^7.16.7",
"@babel/preset-env": "^7.16.8",
"@types/node": "^16.0.0",
"discord.js": "^13.0.0-dev.fe5d56c.1625443439",
"discord.js": "^13.6.0",
"rimraf": "^3.0.2",
"ts-node": "^10.0.0",
"tsup": "^5.11.11",
"typescript": "^4.3.5"
}
}
15 changes: 15 additions & 0 deletions scripts/postbuild.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/* Clean up out dir */

import { readdir, stat } from "node:fs/promises";
import rimraf from "rimraf";

const dirs = await readdir("./out");
const rimrafp = (path) => new Promise((resolve, reject) => {
rimraf(path, (err) => err ? reject(err) : resolve());
});

for (const dirname of dirs) {
const dir = await stat(`./out/${dirname}`, { throwIfNoEntry: false });
if (!dir.isDirectory()) continue;
await rimrafp(`./out/${dirname}`);
}
35 changes: 23 additions & 12 deletions src/Base/ActionRowHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,25 @@ import {
MessageSelectMenuOptions,
MessageSelectOptionData
} from "discord.js";
import { MessageElement } from "../typings/types";
import { MessageElement, ChildrenType } from "../typings/types";

export default function handleData(component: MessageElement) {
export default function handleData(component: MessageElement): DiscordMessageActionRow {
const actionRow = new DiscordMessageActionRow();

component.children?.forEach((child: MessageElement<MessageButtonOptions | MessageSelectMenuOptions>) => {
for (const child of ((Array.isArray(component) ? component : component.children || []) as ChildrenType[])) {
if (Array.isArray(child)) return handleData(child);
switch (child.type) {
case "MessageButton": {
const buttonComponent = new DiscordMessageButton();
const prop = child.props as MessageButtonOptions;

// @ts-expect-error
if (prop.customId) buttonComponent.setCustomId(prop.customId);
if (prop.disabled) buttonComponent.setDisabled(prop.disabled);
if (prop.emoji) buttonComponent.setEmoji(prop.emoji);
if (prop.label) buttonComponent.setLabel(prop.label);
if (prop.style) buttonComponent.setStyle(prop.style);
// @ts-expect-error
if (prop.url) buttonComponent.setURL(prop.url);

actionRow.addComponents(buttonComponent);
Expand All @@ -31,24 +34,32 @@ export default function handleData(component: MessageElement) {
const selectComponent = new DiscordMessageSelectMenu();
const prop = child.props as MessageSelectMenuOptions;

if (prop.customId) selectComponent.setCustomId(prop.customId);
if (prop.disabled) selectComponent.setDisabled(prop.disabled);
if (prop.maxValues) selectComponent.setMaxValues(prop.maxValues);
if (prop.minValues) selectComponent.setMinValues(prop.minValues);
if (prop.placeholder) selectComponent.setPlaceholder(prop.placeholder);
if (prop?.customId) selectComponent.setCustomId(prop.customId);
if (prop?.disabled) selectComponent.setDisabled(prop.disabled);
if (prop?.maxValues) selectComponent.setMaxValues(prop.maxValues);
if (prop?.minValues) selectComponent.setMinValues(prop.minValues);
if (prop?.placeholder) selectComponent.setPlaceholder(prop.placeholder);

// add options
child.children?.forEach((option: MessageElement<MessageSelectOptionData>) => {
selectComponent.addOptions(option.props);
});
const applyOptions = (comp: MessageElement<MessageSelectOptionData>[] | MessageElement<MessageSelectOptionData>[][]): void => {
for (const selectMenuChild of comp) {
if (Array.isArray(selectMenuChild)) {
return applyOptions(selectMenuChild);
}

return void selectComponent.addOptions(selectMenuChild.props);
}
}

applyOptions(child.children || []);

actionRow.addComponents(selectComponent);
}
break;
default:
throw new Error(`Invalid child component type "${child.type}"!`);
}
});
}

return actionRow;
}
5 changes: 5 additions & 0 deletions src/Base/DiscordComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export class DiscordComponents {
}

static fragment(props: null, components: MessageElement[]) {
process.emitWarning("DiscordComponents.fragment is deprecated, use DiscordComponents.Fragment instead!");
return DiscordComponents.Fragment(props, components);
}

static Fragment(props: null, components: MessageElement[]) {
if (props !== null) throw new TypeError("Root fragments may not have props");
const actionRowData: DiscordMessageActionRow[] = [];
const embedData: MessageEmbed[] = [];
Expand Down
22 changes: 18 additions & 4 deletions src/Base/MessageEmbedHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,32 @@ export default function handleData(component: MessageElement) {
component.children?.forEach((child: MessageElement) => {
switch (child.type) {
case "MessageEmbedFields": {
const fields = child.children as MessageElement<EmbedFieldData>[];
embed.addFields(fields.filter(i => i.type === "MessageEmbedField").map(m => m.props));
const fields = child.children as MessageElement<EmbedFieldData>[] | MessageElement<EmbedFieldData>[][];
const applyFields = (fieldSet: MessageElement<EmbedFieldData>[] | MessageElement<EmbedFieldData>[][]): void => {
for (const field of fieldSet) {
if (Array.isArray(field)) return applyFields(field);
if (field.type === "MessageEmbedField") void embed.addField(field.props.name, field.props.value, field.props.inline);
}
}

applyFields(fields);
}
break;
case "MessageEmbedAuthor": {
const data = child.props as MessageEmbedAuthor;
embed.setAuthor(data.name!, data.iconURL, data.url);
embed.setAuthor({
name: data.name,
iconURL: data.iconURL,
url: data.url
})
}
break;
case "MessageEmbedFooter": {
const data = child.props as MessageEmbedFooter;
embed.setFooter(data.text!, data.iconURL);
embed.setFooter({
text: data.text,
iconURL: data.iconURL
});
}
break;
case "MessageEmbedImage": {
Expand Down
10 changes: 7 additions & 3 deletions src/typings/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import {
import type {
ColorResolvable,
MessageEmbedAuthor,
MessageEmbedFooter,
EmbedFieldData,
MessageEmbedImage,
MessageEmbedThumbnail,
MessageButtonOptions,
MessageSelectOptionData
MessageSelectOptionData,
LinkButtonOptions,
MessageSelectMenuOptions
} from "discord.js";

export type ComponentCreateTypes =
Expand Down Expand Up @@ -46,4 +48,6 @@ export interface EmbedProps {
description?: string;
title?: string;
url?: string;
}
}

export type ChildrenType = MessageElement<MessageButtonOptions | LinkButtonOptions | MessageSelectMenuOptions> | MessageElement;
39 changes: 39 additions & 0 deletions test/babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
"use strict";

var _index = require("../out/index");

/** @jsx DiscordComponents.createComponent */

/** @jsxFrag DiscordComponents.Fragment */
var components = _index.DiscordComponents.createComponent(_index.DiscordComponents.Fragment, null, _index.DiscordComponents.createComponent(_index.MessageActionRow, null, Array.from({
length: 5
}, function (_, i) {
return _index.DiscordComponents.createComponent(_index.MessageButton, {
style: "PRIMARY",
label: "Button ".concat(++i),
customId: "btn_".concat(i)
});
})), _index.DiscordComponents.createComponent(_index.MessageActionRow, null, _index.DiscordComponents.createComponent(_index.MessageSelectMenu, {
customId: "123"
}, Array.from({
length: 5
}, function (_, i) {
return _index.DiscordComponents.createComponent(_index.MessageSelectOption, {
description: "Option number ".concat(++i),
label: "Option ".concat(i),
value: i.toString()
});
}))), _index.DiscordComponents.createComponent(_index.MessageEmbed, {
color: "BLURPLE",
title: "Counter"
}, _index.DiscordComponents.createComponent(_index.MessageEmbedFields, null, Array.from({
length: 10
}, function (_, i) {
var counter = ++i;
return _index.DiscordComponents.createComponent(_index.MessageEmbedField, {
name: "Count ".concat(counter),
value: "Counting ".concat(counter)
});
}))));

console.log("BABEL", components);
44 changes: 44 additions & 0 deletions test/babel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @jsx DiscordComponents.createComponent */
/** @jsxFrag DiscordComponents.Fragment */

import {
DiscordComponents,
MessageActionRow,
MessageSelectMenu,
MessageSelectOption,
MessageButton,
MessageEmbed,
MessageEmbedAuthor,
MessageEmbedFields,
MessageEmbedField,
MessageEmbedFooter,
MessageEmbedImage,
MessageEmbedThumbnail,
} from "../out/index";

const components = (
<>
<MessageActionRow>
{Array.from({ length: 5 }, (_, i) => (
<MessageButton style="PRIMARY" label={`Button ${++i}`} customId={`btn_${i}`} />
))}
</MessageActionRow>
<MessageActionRow>
<MessageSelectMenu customId="123">
{Array.from({ length: 5 }, (_, i) => (
<MessageSelectOption description={`Option number ${++i}`} label={`Option ${i}`} value={i.toString()} />
))}
</MessageSelectMenu>
</MessageActionRow>
<MessageEmbed color="BLURPLE" title="Counter">
<MessageEmbedFields>
{Array.from({ length: 10 }, (_, i) => {
const counter = ++i;
return <MessageEmbedField name={`Count ${counter}`} value={`Counting ${counter}`} />
})}
</MessageEmbedFields>
</MessageEmbed>
</>
);

console.log("BABEL", components);

0 comments on commit a47873e

Please sign in to comment.