@amirulabu/create-recurring-rabbit-app 0.0.0-alpha → 0.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +63 -30
- package/package.json +16 -4
- package/templates/default/README.md +22 -21
- package/templates/default/app.config.ts +9 -0
- package/templates/default/docs/adding-features.md +4 -4
- package/templates/default/docs/adr/006-use-tailwind-css-v4-with-shadcn-ui.md +3 -3
- package/templates/default/docs/database.md +10 -6
- package/templates/default/docs/deployment.md +7 -7
- package/templates/default/docs/troubleshooting.md +22 -20
- package/templates/default/drizzle.config.ts +6 -4
- package/templates/default/src/app/__root.tsx +14 -11
- package/templates/default/src/app/api/auth/get-session.ts +26 -0
- package/templates/default/src/app/api/health.ts +44 -0
- package/templates/default/src/app/auth/forgot-password.tsx +3 -3
- package/templates/default/src/app/auth/login.tsx +3 -3
- package/templates/default/src/app/auth/register.tsx +3 -3
- package/templates/default/src/app/auth/reset-password.tsx +5 -5
- package/templates/default/src/app/auth/verify-email.tsx +1 -1
- package/templates/default/src/app/client.tsx +9 -0
- package/templates/default/src/app/dashboard/index.tsx +14 -45
- package/templates/default/src/app/router.ts +4 -0
- package/templates/default/src/app/ssr.tsx +9 -0
- package/templates/default/src/components/features/dashboard/stats-widget.tsx +35 -0
- package/templates/default/src/components/features/dashboard/user-profile.tsx +41 -0
- package/templates/default/src/components/layout/footer.tsx +42 -0
- package/templates/default/src/components/layout/header.tsx +5 -3
- package/templates/default/src/components/layout/sidebar.tsx +3 -3
- package/templates/default/src/components/ui/README.md +80 -0
- package/templates/default/src/lib/api.ts +10 -4
- package/templates/default/src/lib/auth.ts +37 -1
- package/templates/default/src/server/api/routers/dashboard.ts +6 -6
- package/templates/default/src/server/api/trpc.ts +11 -7
- package/templates/default/src/server/auth/config.ts +24 -0
- package/templates/default/src/server/db/migrate.ts +22 -2
- package/templates/default/src/server/db/seed.ts +0 -5
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from 'commander';
|
|
3
|
-
import
|
|
3
|
+
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { existsSync, promises, mkdirSync, readFileSync } from 'fs';
|
|
6
6
|
import ora from 'ora';
|
|
@@ -16,10 +16,10 @@ async function copyTemplateFiles(templateDir, targetDir) {
|
|
|
16
16
|
const entries = await fs.readdir(src);
|
|
17
17
|
await fs.mkdir(dest, { recursive: true });
|
|
18
18
|
for (const entry of entries) {
|
|
19
|
-
await copyRecursive(
|
|
19
|
+
await copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
20
20
|
}
|
|
21
21
|
} else {
|
|
22
|
-
await fs.mkdir(
|
|
22
|
+
await fs.mkdir(path.dirname(dest), { recursive: true });
|
|
23
23
|
await fs.copyFile(src, dest);
|
|
24
24
|
}
|
|
25
25
|
};
|
|
@@ -29,7 +29,7 @@ async function copyDirectory(src, dest) {
|
|
|
29
29
|
await copyTemplateFiles(src, dest);
|
|
30
30
|
}
|
|
31
31
|
async function generatePackageJson(targetDir, config) {
|
|
32
|
-
const packageJsonPath =
|
|
32
|
+
const packageJsonPath = path.join(targetDir, "package.json");
|
|
33
33
|
const packageJson = {
|
|
34
34
|
name: config.name,
|
|
35
35
|
version: config.version ?? "0.1.0",
|
|
@@ -40,6 +40,7 @@ async function generatePackageJson(targetDir, config) {
|
|
|
40
40
|
build: "vinxi build",
|
|
41
41
|
start: "vinxi start",
|
|
42
42
|
"db:generate": "drizzle-kit generate",
|
|
43
|
+
"db:push": "drizzle-kit push",
|
|
43
44
|
"db:migrate": "drizzle-kit migrate",
|
|
44
45
|
"db:studio": "drizzle-kit studio",
|
|
45
46
|
"db:seed": "tsx src/server/db/seed.ts",
|
|
@@ -96,8 +97,30 @@ async function generatePackageJson(targetDir, config) {
|
|
|
96
97
|
tailwindcss: "^3.4.0",
|
|
97
98
|
tsx: "^4.7.0",
|
|
98
99
|
typescript: "^5.3.3",
|
|
99
|
-
vinxi: "^0.
|
|
100
|
+
vinxi: "^0.3.0",
|
|
100
101
|
...config.devDependencies
|
|
102
|
+
},
|
|
103
|
+
pnpm: {
|
|
104
|
+
overrides: {
|
|
105
|
+
"@tanstack/react-router": "~1.120.0",
|
|
106
|
+
"@tanstack/react-start-client": "~1.120.0",
|
|
107
|
+
"@tanstack/react-start-plugin": "~1.120.0",
|
|
108
|
+
"@tanstack/react-start-server": "~1.120.0",
|
|
109
|
+
"@tanstack/router-core": "~1.120.0",
|
|
110
|
+
"@tanstack/router-generator": "~1.120.0",
|
|
111
|
+
"@tanstack/router-plugin": "~1.120.0",
|
|
112
|
+
"@tanstack/start-client-core": "~1.120.0",
|
|
113
|
+
"@tanstack/start-plugin-core": "~1.120.0",
|
|
114
|
+
"@tanstack/start-server-core": "~1.120.0",
|
|
115
|
+
"@tanstack/server-functions-plugin": "~1.120.0",
|
|
116
|
+
"@tanstack/directive-functions-plugin": "~1.120.0",
|
|
117
|
+
"@tanstack/start-api-routes": "~1.120.0",
|
|
118
|
+
"@tanstack/start-server-functions-client": "~1.120.0",
|
|
119
|
+
"@tanstack/start-server-functions-fetcher": "~1.120.0",
|
|
120
|
+
"@tanstack/start-server-functions-handler": "~1.120.0",
|
|
121
|
+
"@tanstack/router-utils": "~1.120.0",
|
|
122
|
+
"@tanstack/history": "~1.120.0"
|
|
123
|
+
}
|
|
101
124
|
}
|
|
102
125
|
};
|
|
103
126
|
await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
|
|
@@ -137,7 +160,7 @@ async function detectPackageManager() {
|
|
|
137
160
|
function getInstallCommand(packageManager) {
|
|
138
161
|
switch (packageManager) {
|
|
139
162
|
case "pnpm":
|
|
140
|
-
return "pnpm install";
|
|
163
|
+
return "pnpm install --ignore-workspace";
|
|
141
164
|
case "yarn":
|
|
142
165
|
return "yarn";
|
|
143
166
|
case "npm":
|
|
@@ -157,25 +180,41 @@ function installDependencies(projectPath, packageManager) {
|
|
|
157
180
|
if (packageManager === "pnpm") {
|
|
158
181
|
const rebuildSpinner = ora("Building native modules...").start();
|
|
159
182
|
try {
|
|
160
|
-
execSync("pnpm rebuild better-sqlite3", {
|
|
183
|
+
execSync("pnpm rebuild --force better-sqlite3", {
|
|
161
184
|
cwd: projectPath,
|
|
162
185
|
stdio: "inherit"
|
|
163
186
|
});
|
|
164
187
|
rebuildSpinner.succeed(chalk2.green("\u2713 Native modules built"));
|
|
165
188
|
} catch {
|
|
166
|
-
rebuildSpinner.warn(
|
|
167
|
-
chalk2.yellow("\u26A0 Native module build failed, trying alternative method...")
|
|
168
|
-
);
|
|
189
|
+
rebuildSpinner.warn(chalk2.yellow("\u26A0 Force rebuild failed, trying pnpm approve-builds..."));
|
|
169
190
|
try {
|
|
170
|
-
execSync("
|
|
191
|
+
execSync("yes | pnpm approve-builds better-sqlite3", {
|
|
192
|
+
cwd: projectPath,
|
|
193
|
+
stdio: "inherit"
|
|
194
|
+
});
|
|
195
|
+
execSync("pnpm rebuild --force better-sqlite3", {
|
|
171
196
|
cwd: projectPath,
|
|
172
197
|
stdio: "inherit"
|
|
173
198
|
});
|
|
174
199
|
rebuildSpinner.succeed(chalk2.green("\u2713 Native modules built"));
|
|
175
200
|
} catch {
|
|
176
201
|
rebuildSpinner.warn(
|
|
177
|
-
chalk2.yellow("\u26A0 Native module build failed,
|
|
202
|
+
chalk2.yellow("\u26A0 Native module build failed, trying alternative method...")
|
|
178
203
|
);
|
|
204
|
+
try {
|
|
205
|
+
execSync("npx node-gyp rebuild --directory node_modules/better-sqlite3", {
|
|
206
|
+
cwd: projectPath,
|
|
207
|
+
stdio: "inherit"
|
|
208
|
+
});
|
|
209
|
+
rebuildSpinner.succeed(chalk2.green("\u2713 Native modules built"));
|
|
210
|
+
} catch {
|
|
211
|
+
rebuildSpinner.fail(chalk2.red("\u2717 Native module build failed"));
|
|
212
|
+
throw new Error(
|
|
213
|
+
`Failed to build native modules. This is required for database functionality.
|
|
214
|
+
Try running manually: cd ${projectPath} && pnpm rebuild --force better-sqlite3
|
|
215
|
+
Or use npm instead: cd ${projectPath} && npm install`
|
|
216
|
+
);
|
|
217
|
+
}
|
|
179
218
|
}
|
|
180
219
|
}
|
|
181
220
|
}
|
|
@@ -201,7 +240,7 @@ function generateSecret(length = 32) {
|
|
|
201
240
|
return crypto.randomBytes(length).toString("base64");
|
|
202
241
|
}
|
|
203
242
|
async function generateEnvFile(targetDir, options = {}) {
|
|
204
|
-
const envPath =
|
|
243
|
+
const envPath = path.join(targetDir, ".env.local");
|
|
205
244
|
const secret = generateSecret(32);
|
|
206
245
|
const content = [
|
|
207
246
|
`# Database`,
|
|
@@ -224,7 +263,7 @@ async function generateEnvFile(targetDir, options = {}) {
|
|
|
224
263
|
await fs.writeFile(envPath, content);
|
|
225
264
|
}
|
|
226
265
|
function loadEnvFile(projectPath) {
|
|
227
|
-
const envPath =
|
|
266
|
+
const envPath = path.join(projectPath, ".env.local");
|
|
228
267
|
const env = {};
|
|
229
268
|
try {
|
|
230
269
|
const content = readFileSync(envPath, "utf-8");
|
|
@@ -247,18 +286,13 @@ function loadEnvFile(projectPath) {
|
|
|
247
286
|
async function initializeDatabase(projectPath) {
|
|
248
287
|
const spinner = ora("Initializing database...").start();
|
|
249
288
|
try {
|
|
250
|
-
const dataDir =
|
|
289
|
+
const dataDir = path.join(projectPath, "data");
|
|
251
290
|
if (!existsSync(dataDir)) {
|
|
252
291
|
mkdirSync(dataDir, { recursive: true });
|
|
253
292
|
spinner.text = "Data directory created";
|
|
254
293
|
}
|
|
255
294
|
const env = loadEnvFile(projectPath);
|
|
256
|
-
await runNpmScript(
|
|
257
|
-
projectPath,
|
|
258
|
-
"./node_modules/.bin/drizzle-kit push",
|
|
259
|
-
"Creating database schema...",
|
|
260
|
-
env
|
|
261
|
-
);
|
|
295
|
+
await runNpmScript(projectPath, "npx drizzle-kit push", "Creating database schema...", env);
|
|
262
296
|
spinner.succeed("Database initialized successfully");
|
|
263
297
|
const seedSpinner = ora("Seeding database with sample data...").start();
|
|
264
298
|
await runSeedScript(projectPath, env);
|
|
@@ -298,14 +332,13 @@ ${errorOutput}`));
|
|
|
298
332
|
}
|
|
299
333
|
async function runSeedScript(projectPath, env) {
|
|
300
334
|
return new Promise((resolve, reject) => {
|
|
301
|
-
const
|
|
302
|
-
const
|
|
303
|
-
const
|
|
304
|
-
const args = process.platform === "win32" ? [tsxPath, seedScriptPath] : [seedScriptPath];
|
|
335
|
+
const seedScriptPath = path.join(projectPath, "src/server/db/seed.ts");
|
|
336
|
+
const command = "npx";
|
|
337
|
+
const args = ["tsx", seedScriptPath];
|
|
305
338
|
const child = spawn(command, args, {
|
|
306
339
|
cwd: projectPath,
|
|
307
340
|
stdio: "inherit",
|
|
308
|
-
shell:
|
|
341
|
+
shell: true,
|
|
309
342
|
env: { ...process.env, ...env }
|
|
310
343
|
});
|
|
311
344
|
child.on("close", (code) => {
|
|
@@ -477,7 +510,7 @@ function validateProjectName(name) {
|
|
|
477
510
|
if (RESERVED_NAMES.has(name)) {
|
|
478
511
|
throw new CLIError(`Invalid project name: "${name}" is a reserved name`);
|
|
479
512
|
}
|
|
480
|
-
const parts = name.split(
|
|
513
|
+
const parts = name.split(path.sep);
|
|
481
514
|
const baseName = parts[parts.length - 1];
|
|
482
515
|
if (baseName !== name) {
|
|
483
516
|
throw new CLIError("Invalid project name: cannot contain path separators");
|
|
@@ -495,15 +528,15 @@ async function cleanupProject(projectPath) {
|
|
|
495
528
|
|
|
496
529
|
// src/commands/create.ts
|
|
497
530
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
498
|
-
var __dirname2 =
|
|
499
|
-
var TEMPLATE_DIR =
|
|
531
|
+
var __dirname2 = path.dirname(__filename2);
|
|
532
|
+
var TEMPLATE_DIR = path.join(__dirname2, "../templates/default");
|
|
500
533
|
async function scaffoldProject(projectName, targetPath) {
|
|
501
534
|
const spinner = ora("Creating project structure...").start();
|
|
502
535
|
let projectPath = "";
|
|
503
536
|
let projectCreated = false;
|
|
504
537
|
try {
|
|
505
538
|
validateProjectName(projectName);
|
|
506
|
-
projectPath =
|
|
539
|
+
projectPath = path.join(targetPath, projectName);
|
|
507
540
|
if (existsSync(projectPath)) {
|
|
508
541
|
spinner.fail(`Directory ${projectName} already exists. Please choose a different name.`);
|
|
509
542
|
throw new Error(`Directory ${projectName} already exists`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amirulabu/create-recurring-rabbit-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"description": "CLI tool to scaffold micro-SaaS apps with TanStack Start, tRPC, Drizzle, and Better-auth",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
"files": [
|
|
13
13
|
"bin",
|
|
14
14
|
"dist",
|
|
15
|
-
"templates"
|
|
15
|
+
"templates",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
".npmrc"
|
|
16
20
|
],
|
|
17
21
|
"scripts": {
|
|
18
22
|
"dev": "tsx src/index.ts",
|
|
@@ -21,7 +25,13 @@
|
|
|
21
25
|
"typecheck": "tsc --noEmit",
|
|
22
26
|
"lint": "eslint . --ext .ts,.tsx",
|
|
23
27
|
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
|
24
|
-
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\""
|
|
28
|
+
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
|
29
|
+
"test": "vitest",
|
|
30
|
+
"test:ui": "vitest --ui",
|
|
31
|
+
"test:run": "vitest run",
|
|
32
|
+
"prepublishOnly": "pnpm build && pnpm test:run",
|
|
33
|
+
"pack": "npm pack --dry-run",
|
|
34
|
+
"publish": "npm publish --provenance false"
|
|
25
35
|
},
|
|
26
36
|
"dependencies": {
|
|
27
37
|
"chalk": "^5.3.0",
|
|
@@ -35,7 +45,9 @@
|
|
|
35
45
|
"@typescript-eslint/parser": "^8.53.1",
|
|
36
46
|
"eslint": "^9.39.2",
|
|
37
47
|
"tsup": "^8.0.1",
|
|
38
|
-
"tsx": "^4.7.0"
|
|
48
|
+
"tsx": "^4.7.0",
|
|
49
|
+
"vitest": "^1.0.4",
|
|
50
|
+
"@vitest/ui": "^1.0.4"
|
|
39
51
|
},
|
|
40
52
|
"engines": {
|
|
41
53
|
"node": ">=18.0.0"
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
# Install dependencies
|
|
9
|
-
|
|
9
|
+
pnpm install
|
|
10
10
|
|
|
11
11
|
# Start development server
|
|
12
|
-
|
|
12
|
+
pnpm dev
|
|
13
13
|
|
|
14
14
|
# Open http://localhost:3000 and create an account
|
|
15
15
|
```
|
|
@@ -37,25 +37,26 @@ src/
|
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
39
|
# Development
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
pnpm dev # Start development server
|
|
41
|
+
pnpm build # Build for production
|
|
42
|
+
pnpm start # Start production server
|
|
43
43
|
|
|
44
44
|
# Bundle Analysis
|
|
45
|
-
|
|
45
|
+
pnpm build:analyze # Build with bundle analyzer (opens stats.html)
|
|
46
46
|
|
|
47
47
|
# Database
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
pnpm db:generate # Generate migration files
|
|
49
|
+
pnpm db:push # Push schema changes to SQLite (recommended for dev)
|
|
50
|
+
pnpm db:migrate # Apply migration files (for PostgreSQL production)
|
|
51
|
+
pnpm db:studio # Open Drizzle Studio
|
|
52
|
+
pnpm db:seed # Seed database with sample data
|
|
52
53
|
|
|
53
54
|
# Code Quality
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
pnpm typecheck # Run TypeScript checks
|
|
56
|
+
pnpm lint # Run ESLint
|
|
57
|
+
pnpm lint:fix # Fix ESLint issues
|
|
58
|
+
pnpm format # Format code with Prettier
|
|
59
|
+
pnpm clean # Clean build artifacts
|
|
59
60
|
```
|
|
60
61
|
|
|
61
62
|
## Environment Setup
|
|
@@ -84,7 +85,7 @@ Optional variables (production):
|
|
|
84
85
|
Analyze your bundle size to identify large dependencies and optimize performance:
|
|
85
86
|
|
|
86
87
|
```bash
|
|
87
|
-
|
|
88
|
+
pnpm build:analyze
|
|
88
89
|
```
|
|
89
90
|
|
|
90
91
|
This will build your application and generate a `stats.html` file in build output directory (`.vinxi`). Open this file in your browser to explore:
|
|
@@ -106,7 +107,7 @@ Monitor application performance to identify bottlenecks:
|
|
|
106
107
|
|
|
107
108
|
```bash
|
|
108
109
|
# Run Lighthouse CI to check performance budgets
|
|
109
|
-
|
|
110
|
+
pnpm lighthouse
|
|
110
111
|
```
|
|
111
112
|
|
|
112
113
|
The project includes performance monitoring tools:
|
|
@@ -160,8 +161,8 @@ export function MyComponent() {
|
|
|
160
161
|
**Database Tables**
|
|
161
162
|
|
|
162
163
|
1. Define schema in `src/server/db/schema.ts`
|
|
163
|
-
2. Generate migration: `
|
|
164
|
-
3. Apply migration: `
|
|
164
|
+
2. Generate migration: `pnpm db:generate`
|
|
165
|
+
3. Apply migration: `pnpm db:push` (SQLite dev) or `pnpm db:generate && pnpm db:migrate` (PostgreSQL prod)
|
|
165
166
|
|
|
166
167
|
```typescript
|
|
167
168
|
export const posts = sqliteTable('posts', {
|
|
@@ -255,7 +256,7 @@ Railway provides built-in PostgreSQL hosting.
|
|
|
255
256
|
When deploying to production with PostgreSQL:
|
|
256
257
|
|
|
257
258
|
```bash
|
|
258
|
-
|
|
259
|
+
pnpm db:generate && pnpm db:migrate
|
|
259
260
|
```
|
|
260
261
|
|
|
261
262
|
Make sure to test migrations locally with PostgreSQL before deploying.
|
|
@@ -307,7 +308,7 @@ Use `drizzle-kit push` instead of `drizzle-kit migrate` for SQLite to avoid mult
|
|
|
307
308
|
If you get "database is locked" errors:
|
|
308
309
|
|
|
309
310
|
```bash
|
|
310
|
-
|
|
311
|
+
pnpm clean
|
|
311
312
|
# Then restart dev server
|
|
312
313
|
```
|
|
313
314
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { defineConfig } from '@tanstack/start/config'
|
|
2
2
|
import { visualizer } from 'rollup-plugin-visualizer'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
3
7
|
|
|
4
8
|
export default defineConfig({
|
|
5
9
|
tsr: {
|
|
@@ -16,5 +20,10 @@ export default defineConfig({
|
|
|
16
20
|
template: 'treemap',
|
|
17
21
|
}),
|
|
18
22
|
],
|
|
23
|
+
resolve: {
|
|
24
|
+
alias: {
|
|
25
|
+
'@': path.resolve(__dirname, './src'),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
19
28
|
},
|
|
20
29
|
})
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
## Creating New Database Tables
|
|
4
4
|
|
|
5
5
|
1. **Define schema** in `src/server/db/schema.ts`
|
|
6
|
-
2. **Generate migration** with `
|
|
7
|
-
3. **Apply migration** with `
|
|
6
|
+
2. **Generate migration** with `pnpm db:generate`
|
|
7
|
+
3. **Apply migration** with `pnpm db:push` (SQLite) or `pnpm db:generate && pnpm db:migrate` (PostgreSQL)
|
|
8
8
|
|
|
9
9
|
Example:
|
|
10
10
|
|
|
@@ -380,7 +380,7 @@ function MyComponent() {
|
|
|
380
380
|
|
|
381
381
|
### Stripe for Payments
|
|
382
382
|
|
|
383
|
-
1. Install Stripe SDK: `
|
|
383
|
+
1. Install Stripe SDK: `pnpm add stripe`
|
|
384
384
|
2. Add environment variable: `STRIPE_SECRET_KEY`
|
|
385
385
|
3. Create webhook handler in `src/app/api/stripe-webhook.ts`
|
|
386
386
|
4. Add procedures to handle payments
|
|
@@ -425,7 +425,7 @@ await resend.emails.send({
|
|
|
425
425
|
|
|
426
426
|
## Testing New Features
|
|
427
427
|
|
|
428
|
-
1. **Add table** → `
|
|
428
|
+
1. **Add table** → `pnpm db:generate && pnpm db:push` (SQLite) or `pnpm db:generate && pnpm db:migrate` (PostgreSQL)
|
|
429
429
|
2. **Add tRPC procedure** → Test in tRPC DevTools
|
|
430
430
|
3. **Add UI component** → Test in isolation
|
|
431
431
|
4. **Add page** → Test navigation and rendering
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ADR-006: Use Tailwind CSS
|
|
1
|
+
# ADR-006: Use Tailwind CSS v3 with shadcn/ui
|
|
2
2
|
|
|
3
3
|
## Status
|
|
4
4
|
|
|
@@ -10,7 +10,7 @@ Need styling system with rapid development and professional components
|
|
|
10
10
|
|
|
11
11
|
## Decision
|
|
12
12
|
|
|
13
|
-
Use Tailwind CSS
|
|
13
|
+
Use Tailwind CSS v3.4 with shadcn/ui components
|
|
14
14
|
|
|
15
15
|
## Consequences
|
|
16
16
|
|
|
@@ -19,4 +19,4 @@ Use Tailwind CSS v4 with shadcn/ui components
|
|
|
19
19
|
|
|
20
20
|
## Implementation
|
|
21
21
|
|
|
22
|
-
Configure Tailwind
|
|
22
|
+
Configure Tailwind v3.4, install shadcn/ui components, use for all UI
|
|
@@ -180,13 +180,17 @@ const db = drizzle(pool, { schema })
|
|
|
180
180
|
|
|
181
181
|
```bash
|
|
182
182
|
# Generate migration files
|
|
183
|
-
|
|
183
|
+
pnpm db:generate
|
|
184
184
|
|
|
185
185
|
# Apply migrations
|
|
186
|
-
|
|
186
|
+
# For SQLite (development): recommended to use push
|
|
187
|
+
pnpm db:push
|
|
188
|
+
|
|
189
|
+
# For PostgreSQL (production): use generate + migrate
|
|
190
|
+
pnpm db:generate && pnpm db:migrate
|
|
187
191
|
|
|
188
192
|
# Open Drizzle Studio to browse database
|
|
189
|
-
|
|
193
|
+
pnpm db:studio
|
|
190
194
|
```
|
|
191
195
|
|
|
192
196
|
### Migration Files
|
|
@@ -324,7 +328,7 @@ Tips:
|
|
|
324
328
|
The seed script creates sample users and dashboard data:
|
|
325
329
|
|
|
326
330
|
```bash
|
|
327
|
-
|
|
331
|
+
pnpm db:seed
|
|
328
332
|
```
|
|
329
333
|
|
|
330
334
|
Seed data location: `src/server/db/seed.ts`
|
|
@@ -340,7 +344,7 @@ Seed data location: `src/server/db/seed.ts`
|
|
|
340
344
|
**Solution:**
|
|
341
345
|
|
|
342
346
|
```bash
|
|
343
|
-
|
|
347
|
+
pnpm clean
|
|
344
348
|
# This removes WAL files and restarts clean
|
|
345
349
|
```
|
|
346
350
|
|
|
@@ -354,7 +358,7 @@ npm run clean
|
|
|
354
358
|
|
|
355
359
|
1. Review migration file in `drizzle/migrations/`
|
|
356
360
|
2. Test migration on backup first
|
|
357
|
-
3.
|
|
361
|
+
3. For schema changes, consider using a new migration instead of rolling back
|
|
358
362
|
|
|
359
363
|
### Slow Queries
|
|
360
364
|
|
|
@@ -64,11 +64,11 @@ railway up
|
|
|
64
64
|
FROM node:18-alpine
|
|
65
65
|
WORKDIR /app
|
|
66
66
|
COPY package*.json ./
|
|
67
|
-
RUN
|
|
67
|
+
RUN pnpm install --frozen-lockfile --prod
|
|
68
68
|
COPY . .
|
|
69
|
-
RUN
|
|
69
|
+
RUN pnpm build
|
|
70
70
|
EXPOSE 3000
|
|
71
|
-
CMD ["
|
|
71
|
+
CMD ["pnpm", "start"]
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
```bash
|
|
@@ -262,13 +262,13 @@ const { data } = api.myQuery.useQuery({
|
|
|
262
262
|
3. **Run Migrations**
|
|
263
263
|
|
|
264
264
|
```bash
|
|
265
|
-
DATABASE_URL="postgres://..."
|
|
265
|
+
DATABASE_URL="postgres://..." pnpm db:generate && pnpm db:migrate
|
|
266
266
|
```
|
|
267
267
|
|
|
268
268
|
4. **Build Production Bundle**
|
|
269
269
|
|
|
270
270
|
```bash
|
|
271
|
-
|
|
271
|
+
pnpm build
|
|
272
272
|
```
|
|
273
273
|
|
|
274
274
|
5. **Deploy**
|
|
@@ -315,8 +315,8 @@ const { data } = api.myQuery.useQuery({
|
|
|
315
315
|
|
|
316
316
|
**Solution:**
|
|
317
317
|
|
|
318
|
-
1. Run `
|
|
319
|
-
2. Run `
|
|
318
|
+
1. Run `pnpm typecheck` locally
|
|
319
|
+
2. Run `pnpm build` locally
|
|
320
320
|
3. Check build logs in platform dashboard
|
|
321
321
|
|
|
322
322
|
#### Email Not Sending in Production
|
|
@@ -17,6 +17,8 @@ pnpm rebuild better-sqlite3
|
|
|
17
17
|
**Solution 2:** Use npm/yarn instead
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
+
pnpm install
|
|
21
|
+
# or
|
|
20
22
|
npm install
|
|
21
23
|
# or
|
|
22
24
|
yarn install
|
|
@@ -32,10 +34,10 @@ yarn install
|
|
|
32
34
|
|
|
33
35
|
```bash
|
|
34
36
|
# Instead of:
|
|
35
|
-
|
|
37
|
+
pnpm db:generate && pnpm db:migrate
|
|
36
38
|
|
|
37
39
|
# Use:
|
|
38
|
-
|
|
40
|
+
pnpm db:push
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
### Database Locked (SQLite)
|
|
@@ -47,7 +49,7 @@ npx drizzle-kit push
|
|
|
47
49
|
**Solution:** Clean build artifacts
|
|
48
50
|
|
|
49
51
|
```bash
|
|
50
|
-
|
|
52
|
+
pnpm clean
|
|
51
53
|
# This removes data/*.db-shm and data/*.db-wal files
|
|
52
54
|
```
|
|
53
55
|
|
|
@@ -70,7 +72,7 @@ netstat -ano | findstr :3000
|
|
|
70
72
|
|
|
71
73
|
```bash
|
|
72
74
|
# In app.config.ts or pass environment variable
|
|
73
|
-
PORT=3001
|
|
75
|
+
PORT=3001 pnpm dev
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
## Installation Issues
|
|
@@ -84,14 +86,14 @@ PORT=3001 npm run dev
|
|
|
84
86
|
**Solution 1:** Clear cache and reinstall
|
|
85
87
|
|
|
86
88
|
```bash
|
|
87
|
-
rm -rf node_modules package-lock.json
|
|
88
|
-
|
|
89
|
+
rm -rf node_modules package-lock.json pnpm-lock.yaml
|
|
90
|
+
pnpm install
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
**Solution 2:** Use different registry
|
|
92
94
|
|
|
93
95
|
```bash
|
|
94
|
-
|
|
96
|
+
pnpm install --registry=https://registry.npmjs.org
|
|
95
97
|
```
|
|
96
98
|
|
|
97
99
|
### TypeScript Errors After Install
|
|
@@ -194,12 +196,12 @@ export const auth = betterAuth({
|
|
|
194
196
|
|
|
195
197
|
### Build Fails with TypeScript Errors
|
|
196
198
|
|
|
197
|
-
**Error:** Type errors during `
|
|
199
|
+
**Error:** Type errors during `pnpm build`
|
|
198
200
|
|
|
199
201
|
**Solution 1:** Run typecheck first
|
|
200
202
|
|
|
201
203
|
```bash
|
|
202
|
-
|
|
204
|
+
pnpm typecheck
|
|
203
205
|
```
|
|
204
206
|
|
|
205
207
|
**Solution 2:** Check for `any` types
|
|
@@ -319,7 +321,7 @@ ls src/app/dashboard/index.tsx
|
|
|
319
321
|
|
|
320
322
|
```bash
|
|
321
323
|
# Stop (Ctrl+C) and restart
|
|
322
|
-
|
|
324
|
+
pnpm dev
|
|
323
325
|
```
|
|
324
326
|
|
|
325
327
|
### Page Load Slow
|
|
@@ -332,7 +334,7 @@ npm run dev
|
|
|
332
334
|
|
|
333
335
|
```bash
|
|
334
336
|
# Run bundle analyzer
|
|
335
|
-
|
|
337
|
+
pnpm build:analyze
|
|
336
338
|
|
|
337
339
|
# This will build and generate stats.html
|
|
338
340
|
# Open the file in your browser to visualize bundle size
|
|
@@ -358,7 +360,7 @@ npm run build:analyze
|
|
|
358
360
|
**Solution 1:** Use the build:analyze script
|
|
359
361
|
|
|
360
362
|
```bash
|
|
361
|
-
|
|
363
|
+
pnpm build:analyze
|
|
362
364
|
```
|
|
363
365
|
|
|
364
366
|
This script automatically sets `ANALYZE_BUNDLE=true` and opens the visualization.
|
|
@@ -367,7 +369,7 @@ This script automatically sets `ANALYZE_BUNDLE=true` and opens the visualization
|
|
|
367
369
|
|
|
368
370
|
```bash
|
|
369
371
|
# Build with analyzer
|
|
370
|
-
ANALYZE_BUNDLE=true
|
|
372
|
+
ANALYZE_BUNDLE=true pnpm build
|
|
371
373
|
|
|
372
374
|
# Then manually open the file
|
|
373
375
|
# Find stats.html in .vinxi or build output directory
|
|
@@ -467,11 +469,11 @@ export default defineConfig({
|
|
|
467
469
|
|
|
468
470
|
```bash
|
|
469
471
|
# Check terminal output for build errors
|
|
470
|
-
|
|
472
|
+
pnpm build:analyze
|
|
471
473
|
|
|
472
474
|
# If there are errors, fix them first
|
|
473
|
-
|
|
474
|
-
|
|
475
|
+
pnpm typecheck
|
|
476
|
+
pnpm lint
|
|
475
477
|
```
|
|
476
478
|
|
|
477
479
|
**Cause 2:** No code or empty routes
|
|
@@ -538,7 +540,7 @@ export default defineConfig({
|
|
|
538
540
|
**Solution:** Run production build locally
|
|
539
541
|
|
|
540
542
|
```bash
|
|
541
|
-
|
|
543
|
+
pnpm build
|
|
542
544
|
# Check for errors
|
|
543
545
|
```
|
|
544
546
|
|
|
@@ -571,7 +573,7 @@ node -v
|
|
|
571
573
|
|
|
572
574
|
**Development:**
|
|
573
575
|
|
|
574
|
-
- Terminal output from `
|
|
576
|
+
- Terminal output from `pnpm dev`
|
|
575
577
|
- Browser console (DevTools)
|
|
576
578
|
- tRPC DevTools
|
|
577
579
|
- React DevTools
|
|
@@ -596,7 +598,7 @@ node -v
|
|
|
596
598
|
- Restart server after changes
|
|
597
599
|
|
|
598
600
|
3. **Check database**
|
|
599
|
-
- Run `
|
|
601
|
+
- Run `pnpm db:studio` to browse database
|
|
600
602
|
- Verify data exists
|
|
601
603
|
- Check for corrupted data
|
|
602
604
|
|
|
@@ -642,7 +644,7 @@ node -v
|
|
|
642
644
|
When reporting issues, include:
|
|
643
645
|
|
|
644
646
|
1. **Node.js version:** `node -v`
|
|
645
|
-
2. **Package manager:** `
|
|
647
|
+
2. **Package manager:** `pnpm -v`
|
|
646
648
|
3. **Operating system:** Mac/Linux/Windows
|
|
647
649
|
4. **Error message:** Full error from console
|
|
648
650
|
5. **Steps to reproduce:** What you did before error
|