14 Commits

Author SHA1 Message Date
github-actions[bot]
1fa4bc800b Version Packages 2022-08-04 10:39:56 -05:00
itsMapleLeaf
e3351654ea changeset 2022-08-04 10:38:08 -05:00
itsMapleLeaf
d1ca002939 fix links, closes #17 2022-08-04 10:38:08 -05:00
itsMapleLeaf
72f4a4afff changeset 2022-07-23 14:42:12 -05:00
itsMapleLeaf
eed5715f1f update website with new remix typings 2022-07-23 14:24:12 -05:00
itsMapleLeaf
e486da0881 migrate to cypress 10 2022-07-23 14:24:12 -05:00
itsMapleLeaf
b275d9b330 update reconciler 2022-07-23 14:24:12 -05:00
itsMapleLeaf
bab134d697 remove vite
was only used for viest config types, don't need it now
2022-07-23 14:24:12 -05:00
itsMapleLeaf
df9bdfaf77 remove nanoid, use crypto.randomUUID()
removes a dependency, and resolves an ESM require error
2022-07-23 14:24:12 -05:00
itsMapleLeaf
35d7f0b33f fix linter warnings 2022-07-23 14:24:12 -05:00
itsMapleLeaf
4f9fb4310f upgrade dependencies 2022-07-23 14:24:12 -05:00
itsMapleLeaf
7b74628732 add link to template + other tweaks 2022-07-23 00:16:27 -05:00
itsMapleLeaf
7536bdee43 changeset 2022-07-22 23:17:03 -05:00
itsMapleLeaf
ef8d915e3b add types field in exports to work with TS NodeNext 2022-07-22 23:15:57 -05:00
30 changed files with 3177 additions and 2309 deletions

View File

@@ -11,13 +11,13 @@
},
"devDependencies": {
"@changesets/cli": "^2.24.0",
"@itsmapleleaf/configs": "^1.1.3",
"@rushstack/eslint-patch": "^1.1.3",
"@types/eslint": "^8.4.1",
"eslint": "^8.14.0",
"node": "^16",
"prettier": "^2.6.2",
"typescript": "^4.6.3"
"@itsmapleleaf/configs": "^1.1.5",
"@rushstack/eslint-patch": "^1.1.4",
"@types/eslint": "^8.4.5",
"eslint": "^8.20.0",
"node": "^16.16.0",
"prettier": "^2.7.1",
"typescript": "^4.7.4"
},
"resolutions": {
"esbuild": "latest"

View File

@@ -1,5 +1,13 @@
# reacord
## 0.5.1
### Patch Changes
- 72f4a4a: upgrade dependencies and remove some unneeded
- 7536bde: add types in exports to work with TS nodenext
- e335165: fix links
## 0.5.0
### Minor Changes

View File

@@ -1,4 +1,4 @@
import { nanoid } from "nanoid"
import { randomUUID } from "node:crypto"
import React from "react"
import { ReacordElement } from "../../internal/element.js"
import type { ComponentInteraction } from "../../internal/interaction"
@@ -43,7 +43,7 @@ export function Button(props: ButtonProps) {
}
class ButtonNode extends Node<ButtonProps> {
private customId = nanoid()
private customId = randomUUID()
// this has text children, but buttons themselves shouldn't yield text
// eslint-disable-next-line class-methods-use-this

View File

@@ -1,4 +1,4 @@
import { nanoid } from "nanoid"
import { randomUUID } from "node:crypto"
import type { ReactNode } from "react"
import React from "react"
import { isInstanceOf } from "../../../helpers/is-instance-of"
@@ -89,7 +89,7 @@ export function Select(props: SelectProps) {
}
class SelectNode extends Node<SelectProps> {
readonly customId = nanoid()
readonly customId = randomUUID()
override modifyMessageOptions(message: MessageOptions): void {
const actionRow: ActionRow = []

View File

@@ -353,6 +353,17 @@ function getDiscordMessageOptions(reacordOptions: MessageOptions) {
}
}
if (component.type === "link") {
return {
type: Discord.ComponentType.Button,
url: component.url,
label: component.label ?? "",
style: Discord.ButtonStyle.Link,
disabled: component.disabled,
emoji: component.emoji,
}
}
if (component.type === "select") {
return {
...component,
@@ -364,7 +375,7 @@ function getDiscordMessageOptions(reacordOptions: MessageOptions) {
}
}
raise(`Unsupported component type: ${component.type}`)
raise(`Unsupported component type: ${(component as any).type}`)
},
),
})),

View File

@@ -47,7 +47,19 @@ export abstract class Reacord {
this.renderers.push(renderer)
const container = reconciler.createContainer(renderer, 0, false, {})
const container = reconciler.createContainer(
renderer,
0,
// eslint-disable-next-line unicorn/no-null
null,
false,
// eslint-disable-next-line unicorn/no-null
null,
"reacord",
() => {},
// eslint-disable-next-line unicorn/no-null
null,
)
const instance: ReacordInstance = {
render: (content: ReactNode) => {

View File

@@ -1,5 +1,6 @@
import type { HostConfig } from "react-reconciler"
import ReactReconciler from "react-reconciler"
import { DefaultEventPriority } from "react-reconciler/constants"
import { raise } from "../../helpers/raise.js"
import { Node } from "./node.js"
import type { Renderer } from "./renderers/renderer"
@@ -20,8 +21,6 @@ const config: HostConfig<
number, // TimeoutHandle,
number // NoTimeout,
> = {
// config
now: Date.now,
supportsMutation: true,
supportsPersistence: false,
supportsHydration: false,
@@ -52,8 +51,13 @@ const config: HostConfig<
},
createTextInstance: (text) => new TextNode(text),
shouldSetTextContent: () => false,
// @ts-expect-error
detachDeletedInstance: (instance) => {},
beforeActiveInstanceBlur: () => {},
afterActiveInstanceBlur: () => {},
// eslint-disable-next-line unicorn/no-null
getInstanceFromNode: (node: any) => null,
// eslint-disable-next-line unicorn/no-null
getInstanceFromScope: (scopeInstance: any) => null,
clearContainer: (renderer) => {
renderer.nodes.clear()
@@ -94,11 +98,14 @@ const config: HostConfig<
resetAfterCommit: (renderer) => {
renderer.render()
},
prepareScopeUpdate: (scopeInstance: any, instance: any) => {},
preparePortalMount: () => raise("Portals are not supported"),
getPublicInstance: () => raise("Refs are currently not supported"),
finalizeInitialChildren: () => false,
getCurrentEventPriority: () => DefaultEventPriority,
}
export const reconciler = ReactReconciler(config)

View File

@@ -2,7 +2,7 @@
"name": "reacord",
"type": "module",
"description": "Create interactive Discord messages using React.",
"version": "0.5.0",
"version": "0.5.1",
"types": "./dist/main.d.ts",
"homepage": "https://reacord.mapleleaf.dev",
"repository": "https://github.com/itsMapleLeaf/reacord.git",
@@ -27,7 +27,8 @@
"exports": {
".": {
"import": "./dist/main.js",
"require": "./dist/main.cjs"
"require": "./dist/main.cjs",
"types": "./dist/main.d.ts"
},
"./package.json": {
"import": "./package.json",
@@ -46,10 +47,9 @@
"dependencies": {
"@types/node": "*",
"@types/react": "*",
"@types/react-reconciler": "^0.26.6",
"nanoid": "^3.3.3",
"react-reconciler": "^0.27.0",
"rxjs": "^7.5.5"
"@types/react-reconciler": "^0.28.0",
"react-reconciler": "^0.29.0",
"rxjs": "^7.5.6"
},
"peerDependencies": {
"discord.js": "^14",
@@ -62,21 +62,20 @@
},
"devDependencies": {
"@types/lodash-es": "^4.17.6",
"c8": "^7.11.2",
"c8": "^7.12.0",
"discord.js": "^14.0.3",
"dotenv": "^16.0.0",
"dotenv": "^16.0.1",
"lodash-es": "^4.17.21",
"nodemon": "^2.0.15",
"prettier": "^2.6.2",
"pretty-ms": "^7.0.1",
"react": "^18.0.0",
"release-it": "^14.14.2",
"tsup": "^5.12.6",
"nodemon": "^2.0.19",
"prettier": "^2.7.1",
"pretty-ms": "^8.0.0",
"react": "^18.2.0",
"release-it": "^15.1.3",
"tsup": "^6.1.3",
"tsx": "^3.8.0",
"type-fest": "^2.12.2",
"typescript": "^4.6.3",
"vite": "^2.9.5",
"vitest": "^0.10.0"
"type-fest": "^2.17.0",
"typescript": "^4.7.4",
"vitest": "^0.18.1"
},
"resolutions": {
"esbuild": "latest"

View File

@@ -6,6 +6,7 @@ import * as React from "react"
import { useState } from "react"
import {
Button,
Link,
Option,
ReacordDiscordJs,
Select,
@@ -132,3 +133,7 @@ await createTest("delete this", (channel) => {
}
reacord.send(channel.id, <DeleteThis />)
})
await createTest("link", (channel) => {
reacord.send(channel.id, <Link label="hi" url="https://mapleleaf.dev" />)
})

View File

@@ -35,7 +35,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("show embed").click()
await tester.findButtonByLabel("show embed").click()
await tester.assertMessages([
{
content: "count: 0",
@@ -62,7 +62,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("clicc").click()
await tester.findButtonByLabel("clicc").click()
await tester.assertMessages([
{
content: "count: 1",
@@ -94,7 +94,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("clicc").click()
await tester.findButtonByLabel("clicc").click()
await tester.assertMessages([
{
content: "count: 2",
@@ -126,7 +126,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("hide embed").click()
await tester.findButtonByLabel("hide embed").click()
await tester.assertMessages([
{
content: "count: 2",
@@ -153,7 +153,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("clicc").click()
await tester.findButtonByLabel("clicc").click()
await tester.assertMessages([
{
content: "count: 3",
@@ -180,7 +180,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("deactivate").click()
await tester.findButtonByLabel("deactivate").click()
await tester.assertMessages([
{
content: "count: 3",
@@ -210,7 +210,7 @@ test("rendering behavior", async () => {
},
])
tester.findButtonByLabel("clicc").click()
await tester.findButtonByLabel("clicc").click()
await tester.assertMessages([
{
content: "count: 3",

View File

@@ -59,16 +59,16 @@ test("single select", async () => {
await assertSelect([])
expect(onSelect).toHaveBeenCalledTimes(0)
tester.findSelectByPlaceholder("choose one").select("2")
await tester.findSelectByPlaceholder("choose one").select("2")
await assertSelect(["2"])
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({ values: ["2"] }),
)
tester.findButtonByLabel("disable").click()
await tester.findButtonByLabel("disable").click()
await assertSelect(["2"], true)
tester.findSelectByPlaceholder("choose one").select("1")
await tester.findSelectByPlaceholder("choose one").select("1")
await assertSelect(["2"], true)
expect(onSelect).toHaveBeenCalledTimes(1)
})
@@ -125,19 +125,19 @@ test("multiple select", async () => {
await assertSelect([])
expect(onSelect).toHaveBeenCalledTimes(0)
tester.findSelectByPlaceholder("select").select("1", "3")
await tester.findSelectByPlaceholder("select").select("1", "3")
await assertSelect(expect.arrayContaining(["1", "3"]) as unknown as string[])
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({ values: expect.arrayContaining(["1", "3"]) }),
)
tester.findSelectByPlaceholder("select").select("2")
await tester.findSelectByPlaceholder("select").select("2")
await assertSelect(expect.arrayContaining(["2"]) as unknown as string[])
expect(onSelect).toHaveBeenCalledWith(
expect.objectContaining({ values: expect.arrayContaining(["2"]) }),
)
tester.findSelectByPlaceholder("select").select()
await tester.findSelectByPlaceholder("select").select()
await assertSelect([])
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ values: [] }))
})
@@ -145,7 +145,7 @@ test("multiple select", async () => {
test("optional onSelect + unknown value", async () => {
const tester = new ReacordTester()
tester.reply().render(<Select placeholder="select" />)
tester.findSelectByPlaceholder("select").select("something")
await tester.findSelectByPlaceholder("select").select("something")
await tester.assertMessages([
{
content: "",

View File

@@ -1,6 +1,6 @@
/* eslint-disable class-methods-use-this */
/* eslint-disable require-await */
import { nanoid } from "nanoid"
import { randomUUID } from "node:crypto"
import { setTimeout } from "node:timers/promises"
import type { ReactNode } from "react"
import { expect } from "vitest"
@@ -194,7 +194,7 @@ class TestCommandInteraction implements CommandInteraction {
}
class TestInteraction {
readonly id = nanoid()
readonly id = randomUUID()
readonly channelId = "test-channel-id"
constructor(

View File

@@ -1,5 +1,5 @@
/// <reference types="vitest" />
import { defineConfig } from "vite"
import { defineConfig } from "vitest/config"
export default defineConfig({
build: {

View File

@@ -1,5 +1,14 @@
# website
## 0.4.2
### Patch Changes
- Updated dependencies [72f4a4a]
- Updated dependencies [7536bde]
- Updated dependencies [e335165]
- reacord@0.5.1
## 0.4.1
### Patch Changes

View File

@@ -2,35 +2,34 @@ import glob from "fast-glob"
import grayMatter from "gray-matter"
import { readFile } from "node:fs/promises"
import { join, parse } from "node:path"
import type { AppLinkProps } from "~/modules/navigation/app-link"
import { z } from "zod"
const guidesFolder = "app/routes/guides"
export type GuideLink = {
title: string
order: number
link: AppLinkProps
}
const frontmatterSchema = z.object({
meta: z.object({
title: z.string(),
description: z.string(),
}),
order: z.number().optional(),
})
export async function loadGuideLinks(): Promise<GuideLink[]> {
export type GuideLink = Awaited<ReturnType<typeof loadGuideLinks>>[0]
export async function loadGuideLinks() {
const guideFiles = await glob(`**/*.md`, { cwd: guidesFolder })
const links: GuideLink[] = await Promise.all(
const links = await Promise.all(
guideFiles.map(async (file) => {
const { data } = grayMatter(await readFile(join(guidesFolder, file)))
let order = data.order
if (!Number.isFinite(order)) {
order = Number.POSITIVE_INFINITY
}
const result = grayMatter(await readFile(join(guidesFolder, file)))
const data = frontmatterSchema.parse(result.data)
return {
title: data.meta?.title,
order,
title: data.meta.title,
order: data.order ?? Number.POSITIVE_INFINITY,
link: {
type: "router",
type: "router" as const,
to: `/guides/${parse(file).name}`,
children: data.meta?.title,
children: data.meta.title,
},
}
}),

View File

@@ -1,8 +1,4 @@
import type {
LinksFunction,
LoaderFunction,
MetaFunction,
} from "@remix-run/node"
import type { LinksFunction, MetaFunction } from "@remix-run/node"
import {
Links,
LiveReload,
@@ -16,7 +12,6 @@ import packageJson from "reacord/package.json"
import bannerUrl from "~/assets/banner.png"
import faviconUrl from "~/assets/favicon.png"
import { GuideLinksProvider } from "~/modules/navigation/guide-links-context"
import type { GuideLink } from "~/modules/navigation/load-guide-links.server"
import { loadGuideLinks } from "~/modules/navigation/load-guide-links.server"
import prismThemeCss from "~/modules/ui/prism-theme.css"
import tailwindCss from "~/modules/ui/tailwind.out.css"
@@ -61,19 +56,14 @@ export const links: LinksFunction = () => [
},
]
type LoaderData = {
guideLinks: GuideLink[]
}
export const loader: LoaderFunction = async () => {
const data: LoaderData = {
export async function loader() {
return {
guideLinks: await loadGuideLinks(),
}
return data
}
export default function App() {
const data: LoaderData = useLoaderData()
const data = useLoaderData<typeof loader>()
return (
<html lang="en" className="bg-slate-900 text-slate-100">
<head>

View File

@@ -7,11 +7,15 @@ meta:
# Getting Started
This guide assumes some familiarity with JavaScript, [React](https://reactjs.org), [Discord.js](https://discord.js.org) and the [Discord API](https://discord.dev). Keep these pages as reference if you need it.
These guides assume some familiarity with JavaScript, [React](https://reactjs.org), [Discord.js](https://discord.js.org) and the [Discord API](https://discord.dev). Keep these pages as reference if you need it.
**Note:** Ensure your project has support for running code with JSX. I recommend using [esno](https://npm.im/esno).
## Setup from template
## Install
[Use this starter template](https://github.com/itsMapleLeaf/reacord-starter) to get off the ground quickly.
## Adding to an existing project
Install Reacord and dependencies:
```bash
# npm
@@ -24,12 +28,10 @@ yarn add reacord react discord.js
pnpm add reacord react discord.js
```
## Setup
Create a Discord.js client and a Reacord instance:
```js
// main.js
// main.jsx
import { Client } from "discord.js"
import { ReacordDiscordJs } from "reacord"
@@ -42,3 +44,10 @@ client.on("ready", () => {
await client.login(process.env.BOT_TOKEN)
```
To use JSX in your code, run it with [tsx](https://npm.im/tsx):
```bash
npm install tsx
tsx main.tsx
```

View File

@@ -1,4 +1,5 @@
---
order: 5
meta:
title: useInstance
description: Using useInstance to get the current instance within a component

View File

@@ -0,0 +1,8 @@
import { defineConfig } from "cypress"
export default defineConfig({
e2e: {
setupNodeEvents(on, config) {},
baseUrl: "http://localhost:3000/",
},
})

View File

@@ -1,3 +0,0 @@
{
"baseUrl": "http://localhost:3000/"
}

View File

@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -1,22 +0,0 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
export default function cypressConfig(on, config) {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@@ -1,26 +0,0 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
import "@testing-library/cypress/add-commands"

View File

@@ -0,0 +1 @@
import "@testing-library/cypress/add-commands"

View File

@@ -0,0 +1 @@
import "./commands"

View File

@@ -1,20 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import "./commands"
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -1,6 +1,6 @@
{
"name": "website",
"version": "0.4.1",
"version": "0.4.2",
"private": true,
"scripts": {
"dev": "concurrently 'typedoc --watch' 'pnpm tailwind -- --watch' 'remix dev'",
@@ -12,41 +12,42 @@
"typecheck": "tsc --noEmit && tsc --project cypress/tsconfig.json --noEmit"
},
"dependencies": {
"@headlessui/react": "^1.6.0",
"@headlessui/react": "^1.6.6",
"@heroicons/react": "^1.0.6",
"@reach/rect": "^0.17.0",
"@remix-run/node": "^1.4.1",
"@remix-run/react": "^1.4.1",
"@remix-run/serve": "^1.4.1",
"@tailwindcss/typography": "^0.5.2",
"clsx": "^1.1.1",
"@remix-run/node": "^1.6.5",
"@remix-run/react": "^1.6.5",
"@remix-run/serve": "^1.6.5",
"@tailwindcss/typography": "^0.5.4",
"clsx": "^1.2.1",
"fast-glob": "^3.2.11",
"gray-matter": "^4.0.3",
"reacord": "workspace:*",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"react-focus-on": "^3.5.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-focus-on": "^3.6.0",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0"
"react-router-dom": "^6.3.0",
"zod": "^3.17.10"
},
"devDependencies": {
"@remix-run/dev": "^1.4.1",
"@remix-run/dev": "^1.6.5",
"@remix-run/node": "^1.4.1",
"@testing-library/cypress": "^8.0.2",
"@testing-library/cypress": "^8.0.3",
"@types/node": "*",
"@types/react": "^18.0.7",
"@types/react-dom": "^18.0.2",
"@types/tailwindcss": "^3.0.10",
"@types/react": "^18.0.15",
"@types/react-dom": "^18.0.6",
"@types/tailwindcss": "^3.0.11",
"@types/wait-on": "^5.3.1",
"autoprefixer": "^10.4.5",
"concurrently": "^7.1.0",
"cypress": "^9.6.0",
"autoprefixer": "^10.4.7",
"concurrently": "^7.3.0",
"cypress": "^10.3.1",
"execa": "^6.1.0",
"postcss": "^8.4.12",
"rehype-prism-plus": "^1.3.2",
"tailwindcss": "^3.0.24",
"typedoc": "^0.22.15",
"typescript": "^4.6.3",
"postcss": "^8.4.14",
"rehype-prism-plus": "^1.4.2",
"tailwindcss": "^3.1.6",
"typedoc": "^0.23.8",
"typescript": "^4.7.4",
"wait-on": "^6.0.1"
},
"sideEffects": false

View File

@@ -6,5 +6,6 @@
"paths": {
"~/*": ["./app/*"]
}
}
},
"include": ["remix.env.d.ts", "**/*.ts", "**/*.tsx"]
}

5118
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff