create-solostack 1.3.5 → 1.3.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-solostack",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "The complete SaaS boilerplate for indie hackers - Next.js 15 with auth, payments, database, and email",
5
5
  "type": "module",
6
6
  "bin": {
@@ -36,35 +36,29 @@ export function createClient() {
36
36
  `;
37
37
  await writeFile(path.join(projectPath, 'src/utils/supabase/client.ts'), clientUtil);
38
38
 
39
- // 2. Server Utility
40
- const serverUtil = `import { createServerClient, type CookieOptions } from '@supabase/ssr'
39
+ // 2. Server Utility (Next.js 15 compatible - async cookies)
40
+ const serverUtil = `import { createServerClient } from '@supabase/ssr'
41
41
  import { cookies } from 'next/headers'
42
42
 
43
- export function createClient(cookieStore: ReturnType<typeof cookies>) {
43
+ export async function createClient() {
44
+ const cookieStore = await cookies()
45
+
44
46
  return createServerClient(
45
47
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
46
48
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
47
49
  {
48
50
  cookies: {
49
- get(name: string) {
50
- return cookieStore.get(name)?.value
51
+ getAll() {
52
+ return cookieStore.getAll()
51
53
  },
52
- set(name: string, value: string, options: CookieOptions) {
53
- try {
54
- cookieStore.set({ name, value, ...options })
55
- } catch (error) {
56
- // The \`set\` method was called from a Server Component.
57
- // This can be ignored if you have middleware refreshing
58
- // user sessions.
59
- }
60
- },
61
- remove(name: string, options: CookieOptions) {
54
+ setAll(cookiesToSet) {
62
55
  try {
63
- cookieStore.set({ name, value: '', ...options })
64
- } catch (error) {
65
- // The \`delete\` method was called from a Server Component.
66
- // This can be ignored if you have middleware refreshing
67
- // user sessions.
56
+ cookiesToSet.forEach(({ name, value, options }) =>
57
+ cookieStore.set(name, value, options)
58
+ )
59
+ } catch {
60
+ // The \`setAll\` method was called from a Server Component.
61
+ // This can be ignored if you have middleware refreshing user sessions.
68
62
  }
69
63
  },
70
64
  },
@@ -163,7 +157,6 @@ export const config = {
163
157
 
164
158
  // 5. Auth Callback Route
165
159
  const routeCallback = `import { NextResponse } from 'next/server'
166
- import { cookies } from 'next/headers'
167
160
  import { createClient } from '@/utils/supabase/server'
168
161
 
169
162
  export async function GET(request: Request) {
@@ -172,8 +165,7 @@ export async function GET(request: Request) {
172
165
  const next = searchParams.get('next') ?? '/'
173
166
 
174
167
  if (code) {
175
- const cookieStore = cookies()
176
- const supabase = createClient(cookieStore)
168
+ const supabase = await createClient()
177
169
  const { error } = await supabase.auth.exchangeCodeForSession(code)
178
170
  if (!error) {
179
171
  return NextResponse.redirect(new URL(next, request.url))
@@ -461,6 +453,71 @@ export default function SignupPage() {
461
453
  `;
462
454
 
463
455
  await writeFile(path.join(projectPath, 'src/app/signup/page.tsx'), signupPage);
456
+
457
+ // 8. Dashboard Layout (Supabase)
458
+ await ensureDir(path.join(projectPath, 'src/app/dashboard'));
459
+
460
+ const dashboardLayout = `import { redirect } from 'next/navigation';
461
+ import Link from 'next/link';
462
+ import { createClient } from '@/utils/supabase/server';
463
+
464
+ export default async function DashboardLayout({
465
+ children,
466
+ }: {
467
+ children: React.ReactNode;
468
+ }) {
469
+ const supabase = await createClient();
470
+ const { data: { user } } = await supabase.auth.getUser();
471
+
472
+ if (!user) {
473
+ redirect('/login');
474
+ }
475
+
476
+ return (
477
+ <div className="min-h-screen bg-gray-50">
478
+ <nav className="bg-white border-b">
479
+ <div className="container mx-auto px-4 py-4">
480
+ <div className="flex items-center justify-between">
481
+ <Link href="/dashboard" className="text-xl font-bold">
482
+ Dashboard
483
+ </Link>
484
+ <div className="flex items-center gap-6">
485
+ <Link href="/dashboard" className="text-sm hover:text-indigo-600">
486
+ Home
487
+ </Link>
488
+ <Link href="/dashboard/billing" className="text-sm hover:text-indigo-600">
489
+ Billing
490
+ </Link>
491
+ <Link href="/dashboard/settings" className="text-sm hover:text-indigo-600">
492
+ Settings
493
+ </Link>
494
+ <Link href="/api/auth/signout" className="text-sm text-red-600 hover:text-red-700">
495
+ Sign Out
496
+ </Link>
497
+ </div>
498
+ </div>
499
+ </div>
500
+ </nav>
501
+ <main>{children}</main>
502
+ </div>
503
+ );
504
+ }
505
+ `;
506
+ await writeFile(path.join(projectPath, 'src/app/dashboard/layout.tsx'), dashboardLayout);
507
+
508
+ // 9. Signout Route (Supabase)
509
+ await ensureDir(path.join(projectPath, 'src/app/api/auth/signout'));
510
+
511
+ const signoutRoute = `import { NextResponse } from 'next/server';
512
+ import { createClient } from '@/utils/supabase/server';
513
+
514
+ export async function GET() {
515
+ const supabase = await createClient();
516
+ await supabase.auth.signOut();
517
+ return NextResponse.redirect(new URL('/login', process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'));
518
+ }
519
+ `;
520
+ await writeFile(path.join(projectPath, 'src/app/api/auth/signout/route.ts'), signoutRoute);
464
521
  }
465
522
 
466
523
  async function generateNextAuth(projectPath) {
package/src/index.js CHANGED
@@ -53,7 +53,7 @@ export async function main() {
53
53
  ╚════════════════════════════════════════════════════════════════╝
54
54
  `));
55
55
 
56
- console.log(chalk.gray(` Version ${orangeBright('1.3.5')} • Built by ${orangeBright('Danish Akhtar')} • ${orangeBright('github.com/danish296')}\n`));
56
+ console.log(chalk.gray(` Version ${orangeBright('1.3.7')} • Built by ${orangeBright('Danish Akhtar')} • ${orangeBright('github.com/danish296')}\n`));
57
57
 
58
58
  // Parse command line arguments
59
59
  program
@@ -210,10 +210,17 @@ export async function main() {
210
210
  await generateDatabase(projectPath, config);
211
211
  spinner.succeed('Configured database (Prisma + PostgreSQL)');
212
212
 
213
- // Generate authentication
213
+ // Generate UI components (before auth so auth-specific layouts can override)
214
+ if (config.includeUI) {
215
+ spinner = ora('Adding UI components').start();
216
+ await generateUI(projectPath);
217
+ spinner.succeed('Added UI components (shadcn/ui)');
218
+ }
219
+
220
+ // Generate authentication (after UI so auth-specific layouts take precedence)
214
221
  spinner = ora('Configuring authentication').start();
215
222
  await generateAuth(projectPath, config.auth);
216
- spinner.succeed('Configured authentication (NextAuth.js)');
223
+ spinner.succeed('Configured authentication');
217
224
 
218
225
  // Generate payments
219
226
  spinner = ora('Configuring payments').start();
@@ -225,13 +232,6 @@ export async function main() {
225
232
  await generateEmails(projectPath, config.email);
226
233
  spinner.succeed('Configured emails (Resend)');
227
234
 
228
- // Generate UI components
229
- if (config.includeUI) {
230
- spinner = ora('Adding UI components').start();
231
- await generateUI(projectPath);
232
- spinner.succeed('Added UI components (shadcn/ui)');
233
- }
234
-
235
235
  // Generate setup & diagnostics
236
236
  spinner = ora('Adding diagnostics page').start();
237
237
  await generateSetup(projectPath, config);