Initial commit
This commit is contained in:
9
packages/config/package.json
Normal file
9
packages/config/package.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "@cms/config",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"exports": {
|
||||
"./tsconfig/base": "./tsconfig/base.json",
|
||||
"./tsconfig/next": "./tsconfig/next.json"
|
||||
}
|
||||
}
|
||||
17
packages/config/tsconfig/base.json
Normal file
17
packages/config/tsconfig/base.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
"resolveJsonModule": true,
|
||||
"allowJs": false,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"isolatedModules": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"baseUrl": "."
|
||||
}
|
||||
}
|
||||
15
packages/config/tsconfig/next.json
Normal file
15
packages/config/tsconfig/next.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "./base.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
22
packages/content/package.json
Normal file
22
packages/content/package.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@cms/content",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"lint": "biome check src",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cms/config": "workspace:*",
|
||||
"@biomejs/biome": "latest",
|
||||
"typescript": "latest"
|
||||
}
|
||||
}
|
||||
23
packages/content/src/index.ts
Normal file
23
packages/content/src/index.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { z } from "zod"
|
||||
|
||||
export const postStatusSchema = z.enum(["draft", "published"])
|
||||
|
||||
export const postSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
title: z.string().min(3).max(180),
|
||||
slug: z.string().min(3).max(180),
|
||||
excerpt: z.string().max(320).optional(),
|
||||
body: z.string().min(1),
|
||||
status: postStatusSchema,
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
})
|
||||
|
||||
export const upsertPostSchema = postSchema.omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
})
|
||||
|
||||
export type Post = z.infer<typeof postSchema>
|
||||
export type UpsertPostInput = z.infer<typeof upsertPostSchema>
|
||||
8
packages/content/tsconfig.json
Normal file
8
packages/content/tsconfig.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@cms/config/tsconfig/base",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
37
packages/db/package.json
Normal file
37
packages/db/package.json
Normal file
@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "@cms/db",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"lint": "biome check src prisma/seed.ts",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit",
|
||||
"db:generate": "bun --env-file=../../.env prisma generate",
|
||||
"db:migrate": "bun --env-file=../../.env prisma migrate dev",
|
||||
"db:push": "bun --env-file=../../.env prisma db push",
|
||||
"db:studio": "bun --env-file=../../.env prisma studio",
|
||||
"db:seed": "bun --env-file=../../.env prisma/seed.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cms/content": "workspace:*",
|
||||
"@prisma/adapter-pg": "latest",
|
||||
"@prisma/client": "latest",
|
||||
"pg": "latest",
|
||||
"zod": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cms/config": "workspace:*",
|
||||
"@biomejs/biome": "latest",
|
||||
"@types/node": "latest",
|
||||
"@types/pg": "latest",
|
||||
"prisma": "latest",
|
||||
"typescript": "latest"
|
||||
},
|
||||
"prisma": {
|
||||
"seed": "bun --env-file=../../.env prisma/seed.ts"
|
||||
}
|
||||
}
|
||||
12
packages/db/prisma.config.ts
Normal file
12
packages/db/prisma.config.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { defineConfig, env } from "prisma/config"
|
||||
|
||||
export default defineConfig({
|
||||
schema: "prisma/schema.prisma",
|
||||
migrations: {
|
||||
path: "prisma/migrations",
|
||||
seed: "bun prisma/seed.ts",
|
||||
},
|
||||
datasource: {
|
||||
url: env("DATABASE_URL"),
|
||||
},
|
||||
})
|
||||
18
packages/db/prisma/schema.prisma
Normal file
18
packages/db/prisma/schema.prisma
Normal file
@ -0,0 +1,18 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
}
|
||||
|
||||
model Post {
|
||||
id String @id @default(uuid())
|
||||
title String
|
||||
slug String @unique
|
||||
excerpt String?
|
||||
body String
|
||||
status String
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
24
packages/db/prisma/seed.ts
Normal file
24
packages/db/prisma/seed.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { db } from "../src/client"
|
||||
|
||||
async function main() {
|
||||
await db.post.upsert({
|
||||
where: { slug: "welcome" },
|
||||
update: {},
|
||||
create: {
|
||||
title: "Welcome to your CMS",
|
||||
slug: "welcome",
|
||||
excerpt: "Your first seeded post",
|
||||
body: "Edit or delete this post from your admin area.",
|
||||
status: "published",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(async () => {
|
||||
await db.$disconnect()
|
||||
})
|
||||
22
packages/db/src/client.ts
Normal file
22
packages/db/src/client.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { PrismaPg } from "@prisma/adapter-pg"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
import { Pool } from "pg"
|
||||
|
||||
const connectionString = process.env.DATABASE_URL
|
||||
|
||||
if (!connectionString) {
|
||||
throw new Error("DATABASE_URL is not set")
|
||||
}
|
||||
|
||||
const pool = new Pool({ connectionString })
|
||||
const adapter = new PrismaPg(pool)
|
||||
|
||||
declare global {
|
||||
var prisma: PrismaClient | undefined
|
||||
}
|
||||
|
||||
export const db = globalThis.prisma ?? new PrismaClient({ adapter })
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
globalThis.prisma = db
|
||||
}
|
||||
2
packages/db/src/index.ts
Normal file
2
packages/db/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { db } from "./client"
|
||||
export { createPost, listPosts } from "./posts"
|
||||
19
packages/db/src/posts.ts
Normal file
19
packages/db/src/posts.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { upsertPostSchema } from "@cms/content"
|
||||
|
||||
import { db } from "./client"
|
||||
|
||||
export async function listPosts() {
|
||||
return db.post.findMany({
|
||||
orderBy: {
|
||||
updatedAt: "desc",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function createPost(input: unknown) {
|
||||
const payload = upsertPostSchema.parse(input)
|
||||
|
||||
return db.post.create({
|
||||
data: payload,
|
||||
})
|
||||
}
|
||||
9
packages/db/tsconfig.json
Normal file
9
packages/db/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@cms/config/tsconfig/base",
|
||||
"compilerOptions": {
|
||||
"noEmit": false,
|
||||
"types": ["node"],
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts", "prisma/**/*.ts"]
|
||||
}
|
||||
16
packages/ui/components.json
Normal file
16
packages/ui/components.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "new-york",
|
||||
"rsc": true,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "",
|
||||
"css": "../../apps/web/src/app/globals.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@cms/ui/components",
|
||||
"utils": "@cms/ui/utils"
|
||||
}
|
||||
}
|
||||
32
packages/ui/package.json
Normal file
32
packages/ui/package.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "@cms/ui",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./button": "./src/components/button.tsx",
|
||||
"./utils": "./src/lib/utils.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.json",
|
||||
"lint": "biome check src",
|
||||
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"class-variance-authority": "latest",
|
||||
"clsx": "latest",
|
||||
"tailwind-merge": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "latest",
|
||||
"react-dom": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cms/config": "workspace:*",
|
||||
"@biomejs/biome": "latest",
|
||||
"@types/react": "latest",
|
||||
"@types/react-dom": "latest",
|
||||
"typescript": "latest"
|
||||
}
|
||||
}
|
||||
34
packages/ui/src/components/button.tsx
Normal file
34
packages/ui/src/components/button.tsx
Normal file
@ -0,0 +1,34 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import type { ButtonHTMLAttributes } from "react"
|
||||
|
||||
import { cn } from "../lib/utils"
|
||||
|
||||
const buttonVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-neutral-900 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-white",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-neutral-900 text-neutral-50 hover:bg-neutral-800",
|
||||
secondary: "bg-neutral-100 text-neutral-900 hover:bg-neutral-200",
|
||||
ghost: "hover:bg-neutral-100 hover:text-neutral-900",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-4 py-2",
|
||||
sm: "h-9 rounded-md px-3",
|
||||
lg: "h-11 rounded-md px-8",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
export interface ButtonProps
|
||||
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof buttonVariants> {}
|
||||
|
||||
export function Button({ className, size, variant, ...props }: ButtonProps) {
|
||||
return <button className={cn(buttonVariants({ variant, size }), className)} {...props} />
|
||||
}
|
||||
2
packages/ui/src/index.ts
Normal file
2
packages/ui/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from "./components/button"
|
||||
export * from "./lib/utils"
|
||||
6
packages/ui/src/lib/utils.ts
Normal file
6
packages/ui/src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
9
packages/ui/tsconfig.json
Normal file
9
packages/ui/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "@cms/config/tsconfig/base",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"noEmit": false,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
Reference in New Issue
Block a user