create-solostack 1.1.0 → 1.2.0
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/package.json +1 -1
- package/src/generators/base.js +188 -65
package/package.json
CHANGED
package/src/generators/base.js
CHANGED
|
@@ -1,59 +1,50 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { writeFile,
|
|
2
|
+
import { writeFile, ensureDir } from '../utils/files.js';
|
|
3
3
|
import { PACKAGE_VERSIONS } from '../constants.js';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
*/
|
|
11
|
-
export async function generateBase(projectPath, projectName, config) {
|
|
12
|
-
// Create directory structure
|
|
5
|
+
export async function generateBase(projectPath, projectName) {
|
|
6
|
+
// Create project directory
|
|
7
|
+
await ensureDir(projectPath);
|
|
8
|
+
|
|
9
|
+
// Create src/app directory
|
|
13
10
|
await ensureDir(path.join(projectPath, 'src/app'));
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
|
|
12
|
+
// Create public directory
|
|
16
13
|
await ensureDir(path.join(projectPath, 'public'));
|
|
17
14
|
|
|
18
|
-
//Generate package.json
|
|
15
|
+
// Generate package.json
|
|
19
16
|
const packageJson = {
|
|
20
17
|
name: projectName,
|
|
21
18
|
version: '0.1.0',
|
|
22
19
|
private: true,
|
|
23
|
-
type:
|
|
20
|
+
type: "module",
|
|
24
21
|
scripts: {
|
|
25
22
|
dev: 'next dev',
|
|
26
23
|
build: 'next build',
|
|
27
24
|
start: 'next start',
|
|
28
25
|
lint: 'next lint',
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
"db:push": "prisma db push",
|
|
27
|
+
"db:seed": "tsx prisma/seed.ts",
|
|
28
|
+
"db:studio": "prisma studio",
|
|
29
|
+
postinstall: "prisma generate"
|
|
32
30
|
},
|
|
33
31
|
dependencies: {
|
|
34
32
|
next: PACKAGE_VERSIONS.next,
|
|
35
33
|
react: PACKAGE_VERSIONS.react,
|
|
36
|
-
'react-dom': PACKAGE_VERSIONS
|
|
37
|
-
'@prisma/client': PACKAGE_VERSIONS['@prisma/client'],
|
|
34
|
+
'react-dom': PACKAGE_VERSIONS['react-dom'],
|
|
38
35
|
'next-auth': PACKAGE_VERSIONS['next-auth'],
|
|
39
|
-
|
|
36
|
+
'@prisma/client': PACKAGE_VERSIONS['@prisma/client'],
|
|
40
37
|
stripe: PACKAGE_VERSIONS.stripe,
|
|
41
38
|
resend: PACKAGE_VERSIONS.resend,
|
|
42
|
-
'@react-email/components': PACKAGE_VERSIONS['@react-email/components'],
|
|
43
39
|
zod: PACKAGE_VERSIONS.zod,
|
|
44
|
-
'react-hook-form': PACKAGE_VERSIONS['react-hook-form'],
|
|
45
|
-
'@hookform/resolvers': PACKAGE_VERSIONS['@hookform/resolvers'],
|
|
46
|
-
'class-variance-authority': PACKAGE_VERSIONS['class-variance-authority'],
|
|
47
|
-
clsx: PACKAGE_VERSIONS.clsx,
|
|
48
|
-
'tailwind-merge': PACKAGE_VERSIONS['tailwind-merge'],
|
|
49
|
-
'tailwindcss-animate': PACKAGE_VERSIONS['tailwindcss-animate'],
|
|
50
40
|
'lucide-react': PACKAGE_VERSIONS['lucide-react'],
|
|
51
|
-
'
|
|
41
|
+
'date-fns': PACKAGE_VERSIONS['date-fns'],
|
|
42
|
+
'clsx': PACKAGE_VERSIONS.clsx,
|
|
43
|
+
'tailwind-merge': PACKAGE_VERSIONS['tailwind-merge'],
|
|
52
44
|
'@radix-ui/react-slot': PACKAGE_VERSIONS['@radix-ui/react-slot'],
|
|
53
|
-
'@radix-ui/react-
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'@auth/prisma-adapter': PACKAGE_VERSIONS['@auth/prisma-adapter'],
|
|
45
|
+
'@radix-ui/react-label': '^2.0.2',
|
|
46
|
+
'class-variance-authority': '^0.7.0',
|
|
47
|
+
'tailwindcss-animate': '^1.0.7',
|
|
57
48
|
},
|
|
58
49
|
devDependencies: {
|
|
59
50
|
typescript: PACKAGE_VERSIONS.typescript,
|
|
@@ -112,7 +103,14 @@ export async function generateBase(projectPath, projectName, config) {
|
|
|
112
103
|
|
|
113
104
|
// Generate next.config.js
|
|
114
105
|
const nextConfig = `/** @type {import('next').NextConfig} */
|
|
106
|
+
import path from 'path';
|
|
107
|
+
import { fileURLToPath } from 'url';
|
|
108
|
+
|
|
109
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
110
|
+
const __dirname = path.dirname(__filename);
|
|
111
|
+
|
|
115
112
|
const nextConfig = {
|
|
113
|
+
outputFileTracingRoot: __dirname,
|
|
116
114
|
experimental: {
|
|
117
115
|
serverActions: {
|
|
118
116
|
bodySizeLimit: '2mb',
|
|
@@ -310,46 +308,171 @@ export default function RootLayout({
|
|
|
310
308
|
await writeFile(path.join(projectPath, 'src/app/layout.tsx'), layoutTsx);
|
|
311
309
|
|
|
312
310
|
// Generate app/page.tsx
|
|
313
|
-
const pageTsx = `
|
|
311
|
+
const pageTsx = `'use client';
|
|
312
|
+
|
|
313
|
+
import { useState, useEffect } from 'react';
|
|
314
|
+
import Link from 'next/link';
|
|
315
|
+
import { Rocket, Users, DollarSign, Code, Database, CreditCard, Mail, Lock, CheckCircle2, Trophy } from 'lucide-react';
|
|
316
|
+
|
|
317
|
+
export default function Home() {
|
|
318
|
+
const [mounted, setMounted] = useState(false);
|
|
319
|
+
const [mrr, setMrr] = useState(0);
|
|
320
|
+
const [users, setUsers] = useState(0);
|
|
321
|
+
const [clicks, setClicks] = useState(0);
|
|
322
|
+
const [shipped, setShipped] = useState(0);
|
|
323
|
+
|
|
324
|
+
useEffect(() => {
|
|
325
|
+
setMounted(true);
|
|
326
|
+
}, []);
|
|
327
|
+
|
|
328
|
+
const ship = () => {
|
|
329
|
+
setClicks(prev => prev + 1);
|
|
330
|
+
setShipped(prev => prev + 1);
|
|
331
|
+
setUsers(prev => prev + Math.floor(Math.random() * 3) + 1);
|
|
332
|
+
|
|
333
|
+
// Revenue logic
|
|
334
|
+
if (Math.random() > 0.7) {
|
|
335
|
+
setMrr(prev => prev + Math.floor(Math.random() * 20) + 10);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
if (!mounted) return (
|
|
340
|
+
<div className="min-h-screen bg-black text-white flex items-center justify-center">
|
|
341
|
+
<div className="animate-pulse">Loading SaaS...</div>
|
|
342
|
+
</div>
|
|
343
|
+
);
|
|
344
|
+
|
|
314
345
|
return (
|
|
315
|
-
<main className="
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
<
|
|
327
|
-
NextAuth.js configured with email and OAuth providers
|
|
328
|
-
</p>
|
|
346
|
+
<main className="min-h-screen bg-black text-white font-sans selection:bg-indigo-500/30 overflow-hidden">
|
|
347
|
+
{/* Background Gradient */}
|
|
348
|
+
<div className="fixed inset-0 z-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-indigo-900/20 via-black to-black pointer-events-none" />
|
|
349
|
+
|
|
350
|
+
<div className="relative z-10 max-w-6xl mx-auto px-4 py-12">
|
|
351
|
+
{/* Header */}
|
|
352
|
+
<header className="flex items-center justify-between mb-16">
|
|
353
|
+
<div className="flex items-center gap-2">
|
|
354
|
+
<div className="h-8 w-8 bg-indigo-600 rounded-lg flex items-center justify-center">
|
|
355
|
+
<Code className="h-5 w-5 text-white" />
|
|
356
|
+
</div>
|
|
357
|
+
<span className="font-bold text-xl tracking-tight">${projectName}</span>
|
|
329
358
|
</div>
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
359
|
+
|
|
360
|
+
{process.env.NODE_ENV === 'development' && (
|
|
361
|
+
<Link
|
|
362
|
+
href="/setup"
|
|
363
|
+
className="text-sm text-zinc-400 hover:text-white transition-colors flex items-center gap-2 border border-zinc-800 rounded-full px-4 py-1.5 hover:border-zinc-600"
|
|
364
|
+
>
|
|
365
|
+
<CheckCircle2 className="h-4 w-4" />
|
|
366
|
+
Check Diagnostics
|
|
367
|
+
</Link>
|
|
368
|
+
)}
|
|
369
|
+
</header>
|
|
370
|
+
|
|
371
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center mb-24">
|
|
372
|
+
{/* Game Section */}
|
|
373
|
+
<div className="space-y-8">
|
|
374
|
+
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-indigo-500/10 text-indigo-400 text-sm border border-indigo-500/20">
|
|
375
|
+
<Rocket className="h-4 w-4" />
|
|
376
|
+
<span>Interactive SaaS Simulator</span>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<h1 className="text-5xl md:text-6xl font-bold tracking-tight leading-none bg-clip-text text-transparent bg-gradient-to-r from-white to-zinc-500">
|
|
380
|
+
Build your SaaS <br />
|
|
381
|
+
<span className="text-indigo-500">in minutes.</span>
|
|
382
|
+
</h1>
|
|
383
|
+
|
|
384
|
+
<p className="text-xl text-zinc-400 max-w-lg leading-relaxed">
|
|
385
|
+
The ultimate Next.js 15 boilerplate for indie hackers. Authentication, Database, Payments, and Email - pre-configured and ready to ship.
|
|
340
386
|
</p>
|
|
387
|
+
|
|
388
|
+
{/* Game UI */}
|
|
389
|
+
<div className="bg-zinc-900/50 backdrop-blur-sm border border-zinc-800 rounded-2xl p-6 shadow-2xl relative overflow-hidden group/game">
|
|
390
|
+
<div className="absolute inset-0 bg-indigo-500/5 group-hover/game:bg-indigo-500/10 transition-colors pointer-events-none" />
|
|
391
|
+
|
|
392
|
+
<div className="grid grid-cols-3 gap-4 mb-6 relative z-10">
|
|
393
|
+
<div className="bg-black/50 p-4 rounded-xl border border-zinc-800">
|
|
394
|
+
<div className="text-zinc-500 text-xs uppercase font-semibold mb-1">MRR</div>
|
|
395
|
+
<div className="text-2xl font-mono text-green-400 flex items-center">
|
|
396
|
+
<DollarSign className="h-5 w-5 mr-1" />
|
|
397
|
+
{mrr}
|
|
398
|
+
</div>
|
|
399
|
+
</div>
|
|
400
|
+
<div className="bg-black/50 p-4 rounded-xl border border-zinc-800">
|
|
401
|
+
<div className="text-zinc-500 text-xs uppercase font-semibold mb-1">Users</div>
|
|
402
|
+
<div className="text-2xl font-mono text-blue-400 flex items-center">
|
|
403
|
+
<Users className="h-5 w-5 mr-1" />
|
|
404
|
+
{users}
|
|
405
|
+
</div>
|
|
406
|
+
</div>
|
|
407
|
+
<div className="bg-black/50 p-4 rounded-xl border border-zinc-800">
|
|
408
|
+
<div className="text-zinc-500 text-xs uppercase font-semibold mb-1">Shipped</div>
|
|
409
|
+
<div className="text-2xl font-mono text-purple-400 flex items-center">
|
|
410
|
+
<Trophy className="h-5 w-5 mr-1" />
|
|
411
|
+
{shipped}
|
|
412
|
+
</div>
|
|
413
|
+
</div>
|
|
414
|
+
</div>
|
|
415
|
+
|
|
416
|
+
<button
|
|
417
|
+
onClick={ship}
|
|
418
|
+
className="w-full bg-indigo-600 hover:bg-indigo-500 text-white font-bold py-4 rounded-xl transition-all active:scale-95 shadow-[0_0_20px_rgba(79,70,229,0.3)] hover:shadow-[0_0_30px_rgba(79,70,229,0.5)] flex items-center justify-center gap-3 group relative z-10"
|
|
419
|
+
>
|
|
420
|
+
<Rocket className="h-6 w-6 group-hover:rotate-12 transition-transform" />
|
|
421
|
+
SHIP FEATURE
|
|
422
|
+
</button>
|
|
423
|
+
|
|
424
|
+
<p className="text-center text-zinc-500 text-xs mt-4 relative z-10">
|
|
425
|
+
Click to simulate your indie hacker journey! 🚀
|
|
426
|
+
</p>
|
|
427
|
+
</div>
|
|
341
428
|
</div>
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
429
|
+
|
|
430
|
+
{/* Cards Section */}
|
|
431
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
432
|
+
<div className="p-6 rounded-2xl bg-zinc-900/30 border border-zinc-800 hover:border-zinc-600 transition-colors group">
|
|
433
|
+
<div className="h-10 w-10 bg-zinc-800 rounded-lg flex items-center justify-center mb-4 group-hover:bg-zinc-700 transition-colors">
|
|
434
|
+
<Lock className="h-5 w-5 text-indigo-400" />
|
|
435
|
+
</div>
|
|
436
|
+
<h3 className="text-lg font-semibold mb-2 text-white">Authentication</h3>
|
|
437
|
+
<p className="text-zinc-400 text-sm">
|
|
438
|
+
NextAuth.js v5 pre-configured with Google & GitHub OAuth.
|
|
439
|
+
</p>
|
|
440
|
+
</div>
|
|
441
|
+
|
|
442
|
+
<div className="p-6 rounded-2xl bg-zinc-900/30 border border-zinc-800 hover:border-zinc-600 transition-colors group">
|
|
443
|
+
<div className="h-10 w-10 bg-zinc-800 rounded-lg flex items-center justify-center mb-4 group-hover:bg-zinc-700 transition-colors">
|
|
444
|
+
<Database className="h-5 w-5 text-emerald-400" />
|
|
445
|
+
</div>
|
|
446
|
+
<h3 className="text-lg font-semibold mb-2 text-white">Database</h3>
|
|
447
|
+
<p className="text-zinc-400 text-sm">
|
|
448
|
+
Prisma ORM with PostgreSQL. Complete schema for Users and Subs.
|
|
449
|
+
</p>
|
|
450
|
+
</div>
|
|
451
|
+
|
|
452
|
+
<div className="p-6 rounded-2xl bg-zinc-900/30 border border-zinc-800 hover:border-zinc-600 transition-colors group">
|
|
453
|
+
<div className="h-10 w-10 bg-zinc-800 rounded-lg flex items-center justify-center mb-4 group-hover:bg-zinc-700 transition-colors">
|
|
454
|
+
<CreditCard className="h-5 w-5 text-pink-400" />
|
|
455
|
+
</div>
|
|
456
|
+
<h3 className="text-lg font-semibold mb-2 text-white">Payments</h3>
|
|
457
|
+
<p className="text-zinc-400 text-sm">
|
|
458
|
+
Stripe integration with Webhook idempotency and Customer Portal.
|
|
459
|
+
</p>
|
|
460
|
+
</div>
|
|
461
|
+
|
|
462
|
+
<div className="p-6 rounded-2xl bg-zinc-900/30 border border-zinc-800 hover:border-zinc-600 transition-colors group">
|
|
463
|
+
<div className="h-10 w-10 bg-zinc-800 rounded-lg flex items-center justify-center mb-4 group-hover:bg-zinc-700 transition-colors">
|
|
464
|
+
<Mail className="h-5 w-5 text-blue-400" />
|
|
465
|
+
</div>
|
|
466
|
+
<h3 className="text-lg font-semibold mb-2 text-white">Emails</h3>
|
|
467
|
+
<p className="text-zinc-400 text-sm">
|
|
468
|
+
Resend + React Email based transactional emails.
|
|
469
|
+
</p>
|
|
470
|
+
</div>
|
|
347
471
|
</div>
|
|
348
472
|
</div>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
</p>
|
|
473
|
+
|
|
474
|
+
<div className="text-center text-zinc-500 text-sm">
|
|
475
|
+
Get started by editing <code className="bg-zinc-900 border border-zinc-700 px-2 py-1 rounded text-zinc-300 font-mono">src/app/page.tsx</code>
|
|
353
476
|
</div>
|
|
354
477
|
</div>
|
|
355
478
|
</main>
|