v1: delete old repo, initalize monorepo structure, config, database, and api packages basic setup
This commit is contained in:
56
packages/biome-config/biome.json
Normal file
56
packages/biome-config/biome.json
Normal file
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.5/schema.json",
|
||||
"root": false,
|
||||
"vcs": {
|
||||
"enabled": false,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": false
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "space",
|
||||
"lineWidth": 100
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"json": {
|
||||
"parser": {
|
||||
"allowTrailingCommas": true,
|
||||
"allowComments": true
|
||||
},
|
||||
"formatter": {
|
||||
"trailingCommas": "none"
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "single",
|
||||
"jsxQuoteStyle": "single",
|
||||
"indentStyle": "space",
|
||||
"lineWidth": 100,
|
||||
"operatorLinebreak": "before",
|
||||
"trailingCommas": "es5"
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"css": {
|
||||
"formatter": { "enabled": true, "indentStyle": "space" }
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
5
packages/biome-config/package.json
Normal file
5
packages/biome-config/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "@concord/biome-config",
|
||||
"version": "0.1.0",
|
||||
"private": true
|
||||
}
|
||||
38
packages/database/.gitignore
vendored
Normal file
38
packages/database/.gitignore
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
# dependencies (bun install)
|
||||
node_modules
|
||||
|
||||
# output
|
||||
out
|
||||
dist
|
||||
*.tgz
|
||||
|
||||
# code coverage
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# logs
|
||||
logs
|
||||
_.log
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# caches
|
||||
.eslintcache
|
||||
.cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# IntelliJ based IDEs
|
||||
.idea
|
||||
|
||||
# Finder (MacOS) folder config
|
||||
.DS_Store
|
||||
|
||||
# prisma recommended
|
||||
generated
|
||||
|
||||
28
packages/database/package.json
Normal file
28
packages/database/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "@concord/database",
|
||||
"scripts": {
|
||||
"db:generate": "prisma generate",
|
||||
"db:push": "prisma db push",
|
||||
"db:studio": "prisma studio",
|
||||
"db:seed": "bun src/seed.ts",
|
||||
"lint": "biome lint .",
|
||||
"format": "biome format --write .",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@concord/biome-config": "workspace:*",
|
||||
"@concord/tsconfig": "workspace:*",
|
||||
"@types/bun": "latest",
|
||||
"bcryptjs": "^3.0.2",
|
||||
"prisma": "^6.17.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.17.0"
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/client.ts"
|
||||
}
|
||||
}
|
||||
234
packages/database/prisma/schema.prisma
Normal file
234
packages/database/prisma/schema.prisma
Normal file
@@ -0,0 +1,234 @@
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
generator client {
|
||||
provider = "prisma-client"
|
||||
output = "../generated"
|
||||
}
|
||||
|
||||
// Enums
|
||||
enum UserStatus {
|
||||
online
|
||||
offline
|
||||
invisible
|
||||
dnd
|
||||
idle
|
||||
}
|
||||
|
||||
enum ChannelType {
|
||||
voice
|
||||
text
|
||||
forum
|
||||
}
|
||||
|
||||
// Core
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
username String @unique @db.VarChar(32)
|
||||
nickname String? @db.VarChar(64)
|
||||
bio String? @db.VarChar(1024)
|
||||
picture String? @db.VarChar(2048)
|
||||
banner String? @db.VarChar(2048)
|
||||
status UserStatus @default(offline)
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @default(now()) @map("updated_at")
|
||||
passwordHash String @db.VarChar(255)
|
||||
isOwner Boolean @default(false)
|
||||
isAdmin Boolean @default(false)
|
||||
|
||||
// Relations
|
||||
RoleAssign RoleAssign[]
|
||||
Message Message[]
|
||||
Reaction Reaction[]
|
||||
UserPing UserPing[]
|
||||
Timeout Timeout[]
|
||||
RefreshToken RefreshToken[]
|
||||
}
|
||||
|
||||
model Server {
|
||||
id String @id @default(cuid())
|
||||
name String @unique @db.VarChar(32)
|
||||
description String? @db.VarChar(1024)
|
||||
icon String? @db.VarChar(2048)
|
||||
|
||||
// FKs
|
||||
headCategoryId String? @unique @map("head_category_id")
|
||||
headCategory Category? @relation("ServerHeadCategory", fields: [headCategoryId], references: [id], onUpdate: NoAction, onDelete: SetNull)
|
||||
|
||||
// Relations
|
||||
Category Category[]
|
||||
Channel Channel[]
|
||||
Role Role[]
|
||||
Timeout Timeout[]
|
||||
}
|
||||
|
||||
model Category {
|
||||
id String @id @default(cuid())
|
||||
name String @unique @db.VarChar(64)
|
||||
|
||||
// FKs (Linked-List)
|
||||
nextCategoryId String? @unique @map("next_category_id")
|
||||
nextCategory Category? @relation("CategoryOrder", fields: [nextCategoryId], references: [id], onUpdate: NoAction, onDelete: SetNull)
|
||||
prevCategory Category? @relation("CategoryOrder")
|
||||
|
||||
serverId String @map("server_id")
|
||||
server Server @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
||||
|
||||
headChannelId String? @unique @map("head_channel_id")
|
||||
headChannel Channel? @relation("CategoryHeadChannel", fields: [headChannelId], references: [id], onUpdate: NoAction, onDelete: SetNull)
|
||||
// Relations
|
||||
serverLink Server? @relation("ServerHeadCategory")
|
||||
}
|
||||
|
||||
model Channel {
|
||||
id String @id @default(cuid())
|
||||
name String @unique @db.VarChar(64)
|
||||
description String? @db.VarChar(1024)
|
||||
type ChannelType @default(text)
|
||||
|
||||
// FKs (Linked-List)
|
||||
nextChannelId String? @unique @map("next_channel_id")
|
||||
nextChannel Channel? @relation("ChannelOrder", fields: [nextChannelId], references: [id], onUpdate: NoAction, onDelete: SetNull)
|
||||
prevChannel Channel? @relation("ChannelOrder")
|
||||
|
||||
serverId String @map("server_id")
|
||||
server Server @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Relations
|
||||
categoryLink Category? @relation("CategoryHeadChannel")
|
||||
Message Message[]
|
||||
ChannelPermissions ChannelPermissions[]
|
||||
}
|
||||
|
||||
model Message {
|
||||
id String @id @default(cuid())
|
||||
text String @db.VarChar(8096)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now())
|
||||
deletedAt DateTime?
|
||||
|
||||
// FKs
|
||||
channelId String @map("channel_id")
|
||||
channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade)
|
||||
|
||||
userId String @map("user_id")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Replies
|
||||
repliesToId String? @map("replies_to_id")
|
||||
repliesTo Message? @relation("Replies", fields: [repliesToId], references: [id], onUpdate: NoAction, onDelete: SetNull)
|
||||
replies Message[] @relation("Replies")
|
||||
|
||||
// Relations
|
||||
Reaction Reaction[]
|
||||
UserPing UserPing[]
|
||||
RolePing RolePing[]
|
||||
Attachment Attachment[]
|
||||
}
|
||||
|
||||
model Role {
|
||||
id String @id @default(cuid())
|
||||
name String @unique @db.VarChar(64)
|
||||
canModifyRoles Boolean @default(false)
|
||||
canCreateChannels Boolean @default(false)
|
||||
canKickUsers Boolean @default(false)
|
||||
canTimeout Boolean @default(false)
|
||||
|
||||
// FKs
|
||||
serverId String @map("server_id")
|
||||
server Server @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
||||
|
||||
// Relations
|
||||
RoleAssign RoleAssign[]
|
||||
ChannelPermissions ChannelPermissions[]
|
||||
RolePing RolePing[]
|
||||
}
|
||||
|
||||
// Supporting
|
||||
model RoleAssign {
|
||||
// Foreign Keys as composite primary key
|
||||
userId String @map("user_id")
|
||||
roleId String @map("role_id")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([userId, roleId])
|
||||
}
|
||||
|
||||
model ChannelPermissions {
|
||||
id String @id @default(cuid())
|
||||
channelId String @map("channel_id")
|
||||
roleId String @map("role_id")
|
||||
channel Channel @relation(fields: [channelId], references: [id], onDelete: Cascade)
|
||||
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model Reaction {
|
||||
id String @id @default(cuid())
|
||||
emoji String
|
||||
messageId String @map("message_id")
|
||||
userId String @map("user_id")
|
||||
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([messageId, userId, emoji])
|
||||
}
|
||||
|
||||
model UserPing {
|
||||
// Foreign Keys as composite primary key
|
||||
userId String @map("user_id")
|
||||
messageId String @map("message_id")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([userId, messageId])
|
||||
}
|
||||
|
||||
model RolePing {
|
||||
// FKs as composite primary key
|
||||
roleId String @map("role_id")
|
||||
messageId String @map("message_id")
|
||||
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
||||
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([roleId, messageId])
|
||||
}
|
||||
|
||||
model Attachment {
|
||||
id String @id @default(cuid())
|
||||
url String @db.VarChar(2000)
|
||||
fileName String @map("file_name") @db.VarChar(255)
|
||||
mimeType String @map("mime_type") @db.VarChar(64)
|
||||
size Int
|
||||
|
||||
// FKs
|
||||
messageId String @map("message_id")
|
||||
message Message @relation(fields: [messageId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model Timeout {
|
||||
timeoutUntil DateTime @map("timeout_until")
|
||||
description String? @db.VarChar(1000)
|
||||
|
||||
// FKs
|
||||
userId String @map("user_id")
|
||||
serverId String @map("server_id")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
server Server @relation(fields: [serverId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@id([userId, serverId])
|
||||
}
|
||||
|
||||
model RefreshToken {
|
||||
id String @id @default(cuid())
|
||||
tokenHash String @unique @db.VarChar(255)
|
||||
createdAt DateTime @default(now())
|
||||
expiresAt DateTime
|
||||
device String? @db.VarChar(255)
|
||||
|
||||
// FKs
|
||||
userId String @map("user_id")
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
15
packages/database/src/client.ts
Normal file
15
packages/database/src/client.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { PrismaClient } from '../generated/client';
|
||||
|
||||
const globalForPrisma = globalThis as unknown as {
|
||||
prisma: PrismaClient | undefined;
|
||||
};
|
||||
|
||||
export const prisma =
|
||||
globalForPrisma.prisma
|
||||
|| new PrismaClient({
|
||||
log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
|
||||
|
||||
export * from '../generated/client';
|
||||
0
packages/database/src/index.ts
Normal file
0
packages/database/src/index.ts
Normal file
163
packages/database/src/seed.ts
Normal file
163
packages/database/src/seed.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { PrismaClient } from './client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
console.log('Start seeding...');
|
||||
|
||||
const hashedPassword = await bcrypt.hash('test', 10);
|
||||
console.log('Generated password hash.');
|
||||
|
||||
// Clean up existing data to ensure a fresh start
|
||||
await prisma.roleAssign.deleteMany();
|
||||
await prisma.message.deleteMany();
|
||||
await prisma.channel.deleteMany();
|
||||
await prisma.category.deleteMany();
|
||||
await prisma.role.deleteMany();
|
||||
await prisma.server.deleteMany();
|
||||
await prisma.user.deleteMany();
|
||||
console.log('Cleaned up old data.');
|
||||
|
||||
// Create Users
|
||||
const alice = await prisma.user.create({
|
||||
data: {
|
||||
username: 'Alice',
|
||||
nickname: 'Alice',
|
||||
passwordHash: hashedPassword,
|
||||
isOwner: true,
|
||||
},
|
||||
});
|
||||
const bob = await prisma.user.create({
|
||||
data: {
|
||||
username: 'Bob',
|
||||
nickname: 'Bobby',
|
||||
passwordHash: hashedPassword,
|
||||
isAdmin: true,
|
||||
},
|
||||
});
|
||||
const charlie = await prisma.user.create({
|
||||
data: {
|
||||
username: 'Charlie',
|
||||
passwordHash: hashedPassword,
|
||||
},
|
||||
});
|
||||
console.log(`Created users: ${alice.username}, ${bob.username}, ${charlie.username}`);
|
||||
|
||||
// Create a Server
|
||||
const server = await prisma.server.create({
|
||||
data: {
|
||||
name: 'TS Lovers',
|
||||
description: 'Static Types Win',
|
||||
},
|
||||
});
|
||||
console.log(`Created server: ${server.name}`);
|
||||
|
||||
// Create Roles for the server
|
||||
const adminRole = await prisma.role.create({
|
||||
data: {
|
||||
name: 'Admin',
|
||||
serverId: server.id,
|
||||
canCreateChannels: true,
|
||||
canKickUsers: true,
|
||||
canModifyRoles: true,
|
||||
canTimeout: true,
|
||||
},
|
||||
});
|
||||
const memberRole = await prisma.role.create({
|
||||
data: { name: 'Member', serverId: server.id },
|
||||
});
|
||||
console.log(`Created roles: ${adminRole.name}, ${memberRole.name}`);
|
||||
|
||||
// Assign roles to users
|
||||
await prisma.roleAssign.createMany({
|
||||
data: [
|
||||
{ userId: alice.id, roleId: adminRole.id },
|
||||
{ userId: bob.id, roleId: adminRole.id },
|
||||
{ userId: charlie.id, roleId: memberRole.id },
|
||||
],
|
||||
});
|
||||
console.log('Assigned roles to users.');
|
||||
|
||||
// Create Categories
|
||||
const generalCategory = await prisma.category.create({
|
||||
data: {
|
||||
name: 'General',
|
||||
serverId: server.id,
|
||||
},
|
||||
});
|
||||
const devCategory = await prisma.category.create({
|
||||
data: {
|
||||
name: 'Development',
|
||||
serverId: server.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Set the category order (linked-list) and head category for the server
|
||||
await prisma.server.update({
|
||||
where: { id: server.id },
|
||||
data: { headCategoryId: generalCategory.id },
|
||||
});
|
||||
await prisma.category.update({
|
||||
where: { id: generalCategory.id },
|
||||
data: { nextCategoryId: devCategory.id },
|
||||
});
|
||||
console.log(`Created categories: ${generalCategory.name}, ${devCategory.name}`);
|
||||
|
||||
// Create Channels
|
||||
const welcomeChannel = await prisma.channel.create({
|
||||
data: {
|
||||
name: 'welcome',
|
||||
serverId: server.id,
|
||||
},
|
||||
});
|
||||
const announcementsChannel = await prisma.channel.create({
|
||||
data: {
|
||||
name: 'announcements',
|
||||
serverId: server.id,
|
||||
},
|
||||
});
|
||||
|
||||
// Set channel order for the "General" category
|
||||
await prisma.category.update({
|
||||
where: { id: generalCategory.id },
|
||||
data: { headChannelId: welcomeChannel.id },
|
||||
});
|
||||
await prisma.channel.update({
|
||||
where: { id: welcomeChannel.id },
|
||||
data: { nextChannelId: announcementsChannel.id },
|
||||
});
|
||||
console.log(`Created channels: ${welcomeChannel.name}, ${announcementsChannel.name}`);
|
||||
|
||||
// Create a Message
|
||||
const firstMessage = await prisma.message.create({
|
||||
data: {
|
||||
text: `Welcome to the ${server.name} server, ${bob.username}!`,
|
||||
channelId: welcomeChannel.id,
|
||||
userId: alice.id,
|
||||
},
|
||||
});
|
||||
console.log(`Created a welcome message.`);
|
||||
|
||||
// Create a Reply
|
||||
await prisma.message.create({
|
||||
data: {
|
||||
text: 'Glad to be here!',
|
||||
channelId: welcomeChannel.id,
|
||||
userId: bob.id,
|
||||
repliesToId: firstMessage.id,
|
||||
},
|
||||
});
|
||||
console.log(`Created a reply.`);
|
||||
|
||||
console.log('Seeding finished.');
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
5
packages/database/tsconfig.json
Normal file
5
packages/database/tsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"extends": "@concord/tsconfig/base.json",
|
||||
"include": ["."]
|
||||
"exclude": ["generated"]
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# `@turbo/eslint-config`
|
||||
|
||||
Collection of internal eslint configurations.
|
||||
@@ -1,32 +0,0 @@
|
||||
import js from "@eslint/js";
|
||||
import eslintConfigPrettier from "eslint-config-prettier";
|
||||
import turboPlugin from "eslint-plugin-turbo";
|
||||
import tseslint from "typescript-eslint";
|
||||
import onlyWarn from "eslint-plugin-only-warn";
|
||||
|
||||
/**
|
||||
* A shared ESLint configuration for the repository.
|
||||
*
|
||||
* @type {import("eslint").Linter.Config[]}
|
||||
* */
|
||||
export const config = [
|
||||
js.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
plugins: {
|
||||
turbo: turboPlugin,
|
||||
},
|
||||
rules: {
|
||||
"turbo/no-undeclared-env-vars": "warn",
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
onlyWarn,
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: ["dist/**"],
|
||||
},
|
||||
];
|
||||
@@ -1,49 +0,0 @@
|
||||
import js from "@eslint/js";
|
||||
import eslintConfigPrettier from "eslint-config-prettier";
|
||||
import tseslint from "typescript-eslint";
|
||||
import pluginReactHooks from "eslint-plugin-react-hooks";
|
||||
import pluginReact from "eslint-plugin-react";
|
||||
import globals from "globals";
|
||||
import pluginNext from "@next/eslint-plugin-next";
|
||||
import { config as baseConfig } from "./base.js";
|
||||
|
||||
/**
|
||||
* A custom ESLint configuration for libraries that use Next.js.
|
||||
*
|
||||
* @type {import("eslint").Linter.Config[]}
|
||||
* */
|
||||
export const nextJsConfig = [
|
||||
...baseConfig,
|
||||
js.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
...pluginReact.configs.flat.recommended,
|
||||
languageOptions: {
|
||||
...pluginReact.configs.flat.recommended.languageOptions,
|
||||
globals: {
|
||||
...globals.serviceworker,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
"@next/next": pluginNext,
|
||||
},
|
||||
rules: {
|
||||
...pluginNext.configs.recommended.rules,
|
||||
...pluginNext.configs["core-web-vitals"].rules,
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
"react-hooks": pluginReactHooks,
|
||||
},
|
||||
settings: { react: { version: "detect" } },
|
||||
rules: {
|
||||
...pluginReactHooks.configs.recommended.rules,
|
||||
// React scope no longer necessary with new JSX transform.
|
||||
"react/react-in-jsx-scope": "off",
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"name": "@repo/eslint-config",
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"exports": {
|
||||
"./base": "./base.js",
|
||||
"./next-js": "./next.js",
|
||||
"./react-internal": "./react-internal.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.34.0",
|
||||
"@next/eslint-plugin-next": "^15.5.0",
|
||||
"eslint": "^9.34.0",
|
||||
"eslint-config-prettier": "^10.1.1",
|
||||
"eslint-plugin-only-warn": "^1.1.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-turbo": "^2.5.0",
|
||||
"globals": "^16.3.0",
|
||||
"typescript": "^5.9.2",
|
||||
"typescript-eslint": "^8.40.0"
|
||||
}
|
||||
}
|
||||
39
packages/eslint-config/react-internal.js
vendored
39
packages/eslint-config/react-internal.js
vendored
@@ -1,39 +0,0 @@
|
||||
import js from "@eslint/js";
|
||||
import eslintConfigPrettier from "eslint-config-prettier";
|
||||
import tseslint from "typescript-eslint";
|
||||
import pluginReactHooks from "eslint-plugin-react-hooks";
|
||||
import pluginReact from "eslint-plugin-react";
|
||||
import globals from "globals";
|
||||
import { config as baseConfig } from "./base.js";
|
||||
|
||||
/**
|
||||
* A custom ESLint configuration for libraries that use React.
|
||||
*
|
||||
* @type {import("eslint").Linter.Config[]} */
|
||||
export const config = [
|
||||
...baseConfig,
|
||||
js.configs.recommended,
|
||||
eslintConfigPrettier,
|
||||
...tseslint.configs.recommended,
|
||||
pluginReact.configs.flat.recommended,
|
||||
{
|
||||
languageOptions: {
|
||||
...pluginReact.configs.flat.recommended.languageOptions,
|
||||
globals: {
|
||||
...globals.serviceworker,
|
||||
...globals.browser,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
"react-hooks": pluginReactHooks,
|
||||
},
|
||||
settings: { react: { version: "detect" } },
|
||||
rules: {
|
||||
...pluginReactHooks.configs.recommended.rules,
|
||||
// React scope no longer necessary with new JSX transform.
|
||||
"react/react-in-jsx-scope": "off",
|
||||
},
|
||||
},
|
||||
];
|
||||
39
packages/tsconfig/base.json
Normal file
39
packages/tsconfig/base.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"display": "Default",
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"noEmit": true,
|
||||
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"resolveJsonModule": true,
|
||||
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
},
|
||||
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noImplicitOverride": true,
|
||||
"noImplicitReturns": true,
|
||||
"inlineSources": false,
|
||||
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noPropertyAccessFromIndexSignature": false
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
5
packages/tsconfig/package.json
Normal file
5
packages/tsconfig/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "@concord/tsconfig",
|
||||
"version": "0.1.0",
|
||||
"private": true
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"esModuleInterop": true,
|
||||
"incremental": false,
|
||||
"isolatedModules": true,
|
||||
"lib": ["es2022", "DOM", "DOM.Iterable"],
|
||||
"module": "NodeNext",
|
||||
"moduleDetection": "force",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "ES2022"
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"plugins": [{ "name": "next" }],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"allowJs": true,
|
||||
"jsx": "preserve",
|
||||
"noEmit": true
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "@repo/typescript-config",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx"
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import { config } from "@repo/eslint-config/react-internal";
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
export default config;
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"name": "@repo/ui",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"exports": {
|
||||
"./*": "./src/*.tsx"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . --max-warnings 0",
|
||||
"generate:component": "turbo gen react-component",
|
||||
"check-types": "tsc --noEmit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/eslint-config": "*",
|
||||
"@repo/typescript-config": "*",
|
||||
"@types/node": "^22.15.3",
|
||||
"@types/react": "19.1.0",
|
||||
"@types/react-dom": "19.1.1",
|
||||
"eslint": "^9.34.0",
|
||||
"typescript": "5.9.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { ReactNode } from "react";
|
||||
|
||||
interface ButtonProps {
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
appName: string;
|
||||
}
|
||||
|
||||
export const Button = ({ children, className, appName }: ButtonProps) => {
|
||||
return (
|
||||
<button
|
||||
className={className}
|
||||
onClick={() => alert(`Hello from your ${appName} app!`)}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,27 +0,0 @@
|
||||
import { type JSX } from "react";
|
||||
|
||||
export function Card({
|
||||
className,
|
||||
title,
|
||||
children,
|
||||
href,
|
||||
}: {
|
||||
className?: string;
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
href: string;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<a
|
||||
className={className}
|
||||
href={`${href}?utm_source=create-turbo&utm_medium=basic&utm_campaign=create-turbo"`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<h2>
|
||||
{title} <span>-></span>
|
||||
</h2>
|
||||
<p>{children}</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { type JSX } from "react";
|
||||
|
||||
export function Code({
|
||||
children,
|
||||
className,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}): JSX.Element {
|
||||
return <code className={className}>{children}</code>;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/react-library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
Reference in New Issue
Block a user