diff --git a/frameworks/react-cra/add-ons/drizzle/assets/_dot_env.local.append.ejs b/frameworks/react-cra/add-ons/drizzle/assets/_dot_env.local.append.ejs new file mode 100644 index 00000000..2a31bf9b --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/assets/_dot_env.local.append.ejs @@ -0,0 +1,7 @@ +<% if (addOnOption.drizzle.database === 'postgresql') { %> +# Database URL for PostgreSQL +DATABASE_URL="postgresql://username:password@localhost:5432/mydb"<% } else if (addOnOption.drizzle.database === 'mysql') { %> +# Database URL for MySQL +DATABASE_URL="mysql://username:password@localhost:3306/mydb"<% } else if (addOnOption.drizzle.database === 'sqlite') { %> +# Database URL for SQLite +DATABASE_URL="file:./dev.db"<% } %> \ No newline at end of file diff --git a/frameworks/react-cra/add-ons/drizzle/assets/drizzle.config.ts.ejs b/frameworks/react-cra/add-ons/drizzle/assets/drizzle.config.ts.ejs new file mode 100644 index 00000000..d9b9a7eb --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/assets/drizzle.config.ts.ejs @@ -0,0 +1,13 @@ +import { config } from "dotenv"; +import { defineConfig } from 'drizzle-kit'; + +config(); + +export default defineConfig({ + out: "./drizzle", + schema: "./src/db/schema.ts", + dialect: '<%= addOnOption.drizzle.database %>', + dbCredentials: { + url: process.env.DATABASE_URL, + }, +}); diff --git a/frameworks/react-cra/add-ons/drizzle/assets/public/drizzle.svg b/frameworks/react-cra/add-ons/drizzle/assets/public/drizzle.svg new file mode 100644 index 00000000..d9b8730b --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/assets/public/drizzle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frameworks/react-cra/add-ons/drizzle/assets/src/db/index.ts.ejs b/frameworks/react-cra/add-ons/drizzle/assets/src/db/index.ts.ejs new file mode 100644 index 00000000..d9d52de7 --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/assets/src/db/index.ts.ejs @@ -0,0 +1,27 @@ +import { config } from 'dotenv' +<% if (addOnOption.drizzle.database === 'postgresql') { %> +import { drizzle } from 'drizzle-orm/node-postgres'; +import { Pool } from 'pg'; +<% } else if (addOnOption.drizzle.database === 'mysql') {%> +import { drizzle } from 'drizzle-orm/mysql2'; +import mysql from 'mysql2/promise'; +<% } else if (addOnOption.drizzle.database === 'sqlite') {%> +import { drizzle } from 'drizzle-orm/better-sqlite3'; +import Database from 'better-sqlite3'; +<% } %> +import * as schema from './schema.ts' + +config() + +<% if (addOnOption.drizzle.database === 'sqlite') { %> +const sqlite = new Database(process.env.DATABASE_URL!); +export const db = drizzle(sqlite, { schema }); +<% } else if (addOnOption.drizzle.database === 'postgresql') { %> +const pool = new Pool({ + connectionString: process.env.DATABASE_URL!, +}); +export const db = drizzle(pool, { schema }); +<% } else if (addOnOption.drizzle.database === 'mysql') { %> +const connection = await mysql.createConnection(process.env.DATABASE_URL!); +export const db = drizzle(connection, { schema }); +<% } %> diff --git a/frameworks/react-cra/add-ons/drizzle/assets/src/db/schema.ts.ejs b/frameworks/react-cra/add-ons/drizzle/assets/src/db/schema.ts.ejs new file mode 100644 index 00000000..3159477a --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/assets/src/db/schema.ts.ejs @@ -0,0 +1,32 @@ +<% if (addOnOption.drizzle.database === 'postgresql') { %> +import { pgTable, serial, text, timestamp } from +'drizzle-orm/pg-core'; + +export const todos = pgTable('todos', { + id: serial('id').primaryKey(), + title: text('title').notNull(), + createdAt: timestamp('created_at').defaultNow(), +}); +<% } else if (addOnOption.drizzle.database === 'mysql') { +%> +import { mysqlTable, int, text, timestamp } from +'drizzle-orm/mysql-core'; + +export const todos = mysqlTable('todos', { + id: int('id').primaryKey().autoincrement(), + title: text('title').notNull(), + createdAt: timestamp('created_at', { mode: 'date' }).defaultNow(), +}); +<% } else if (addOnOption.drizzle.database === 'sqlite') { +%> +import { sqliteTable, integer, text } from +'drizzle-orm/sqlite-core'; +import { sql } from 'drizzle-orm'; + +export const todos = sqliteTable('todos', { + id: integer('id', { mode: 'number' }).primaryKey({ +autoIncrement: true }), + title: text('title').notNull(), + createdAt: integer('created_at', { mode: 'timestamp' }).default(sql`(unixepoch())`), +}); +<% } %> \ No newline at end of file diff --git a/frameworks/react-cra/add-ons/drizzle/assets/src/routes/demo/drizzle.tsx b/frameworks/react-cra/add-ons/drizzle/assets/src/routes/demo/drizzle.tsx new file mode 100644 index 00000000..536cee22 --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/assets/src/routes/demo/drizzle.tsx @@ -0,0 +1,187 @@ +import { createFileRoute, useRouter } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' +import { db } from '@/db' +import { desc } from 'drizzle-orm' +import { todos } from '@/db/schema' + +const getTodos = createServerFn({ + method: 'GET', +}).handler(async () => { + return await db.query.todos.findMany({ + orderBy: [desc(todos.createdAt)], + }) +}) + +const createTodo = createServerFn({ + method: 'POST', +}) + .inputValidator((data: { title: string }) => data) + .handler(async ({ data }) => { + await db.insert(todos).values({ title: data.title }) + return { success: true } + }) + +export const Route = createFileRoute('/demo/drizzle')({ + component: DemoDrizzle, + loader: async () => await getTodos(), +}) + +function DemoDrizzle() { + const router = useRouter() + const todos = Route.useLoaderData() + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + const formData = new FormData(e.target as HTMLFormElement) + const title = formData.get('title') as string + + if (!title) return + + try { + await createTodo({ data: { title } }) + router.invalidate() + ;(e.target as HTMLFormElement).reset() + } catch (error) { + console.error('Failed to create todo:', error) + } + } + + return ( +
+
+
+
+
+
+ Drizzle Logo +
+
+

+ Drizzle Database Demo +

+
+ +

Todos

+ +
    + {todos.map((todo) => ( +
  • +
    + + {todo.title} + + #{todo.id} +
    +
  • + ))} + {todos.length === 0 && ( +
  • + No todos yet. Create one below! +
  • + )} +
+ +
+ + +
+ +
+

+ Powered by Drizzle ORM +

+

+ Next-generation ORM for Node.js & TypeScript with PostgreSQL +

+
+

Setup Instructions:

+
    +
  1. + Configure your{' '} + + DATABASE_URL + {' '} + in .env.local +
  2. +
  3. + Run:{' '} + + npx drizzle-kit generate + +
  4. +
  5. + Run:{' '} + + npx drizzle-kit migrate + +
  6. +
  7. + Optional:{' '} + + npx drizzle-kit studio + +
  8. +
+
+
+
+
+ ) +} diff --git a/frameworks/react-cra/add-ons/drizzle/info.json b/frameworks/react-cra/add-ons/drizzle/info.json new file mode 100644 index 00000000..62e4efe5 --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/info.json @@ -0,0 +1,32 @@ +{ + "name": "Drizzle", + "description": "Add Drizzle ORM to your application.", + "phase": "add-on", + "type": "add-on", + "priority": 48, + "link": "https://orm.drizzle.team/", + "modes": ["file-router"], + "dependsOn": ["start"], + "options": { + "database": { + "type": "select", + "label": "Database Provider", + "description": "Choose your database provider", + "default": "postgresql", + "options": [ + { "value": "postgresql", "label": "PostgreSQL" }, + { "value": "sqlite", "label": "SQLite" }, + { "value": "mysql", "label": "MySQL" } + ] + } + }, + "routes": [ + { + "icon": "Database", + "url": "/demo/drizzle", + "name": "Drizzle", + "path": "src/routes/demo/drizzle.tsx", + "jsName": "DemoDrizzle" + } + ] +} diff --git a/frameworks/react-cra/add-ons/drizzle/package.json.ejs b/frameworks/react-cra/add-ons/drizzle/package.json.ejs new file mode 100644 index 00000000..1caeeabd --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/package.json.ejs @@ -0,0 +1,24 @@ +{ + "dependencies": { + "drizzle-orm": "^0.39.0", + "drizzle-kit": "^0.30.0"<% if (addOnOption.drizzle.database === 'postgresql') { %>, + "pg": "^8.11.0"<% } %><% if (addOnOption.drizzle.database === 'mysql') { %>, + "mysql2": "^3.6.0"<% } %><% if (addOnOption.drizzle.database === 'sqlite') { %>, + "better-sqlite3": "^9.4.0"<% } %> + }, + "devDependencies": { + "dotenv": "^16.0.0", + "tsx": "^4.0.0", + <% if (addOnOption.drizzle.database === 'postgresql') { %> + "@types/pg": "^8.10.0"<% } %><% if (addOnOption.drizzle.database === 'mysql') { %> + "@types/mysql2": "^3.6.0"<% } %><% if (addOnOption.drizzle.database === 'sqlite') { %> + "@types/better-sqlite3": "^7.6.0"<% } %> + }, + "scripts": { + "db:generate": "drizzle-kit generate", + "db:migrate": "drizzle-kit migrate", + "db:push": "drizzle-kit push", + "db:pull": "drizzle-kit pull", + "db:studio": "drizzle-kit studio" + } +} diff --git a/frameworks/react-cra/add-ons/drizzle/small-logo.svg b/frameworks/react-cra/add-ons/drizzle/small-logo.svg new file mode 100644 index 00000000..1d7ced15 --- /dev/null +++ b/frameworks/react-cra/add-ons/drizzle/small-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/cta-engine/src/create-app.ts b/packages/cta-engine/src/create-app.ts index 6b023be8..8da27f10 100644 --- a/packages/cta-engine/src/create-app.ts +++ b/packages/cta-engine/src/create-app.ts @@ -251,7 +251,7 @@ Use the following commands to start your app: getPackageManagerScriptCommand(options.packageManager, ['dev']), )} -Please read the README.md for information on testing, styling, adding routes, etc.${errorStatement}`, +Please check the README.md for information on testing, styling, adding routes, etc.${errorStatement}`, ) }