nextjs-hackathon-stack 0.1.0 → 0.1.2

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 CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
+ import * as p4 from "@clack/prompts";
5
6
 
6
7
  // src/cli.ts
7
8
  import * as p2 from "@clack/prompts";
@@ -17,9 +18,6 @@ function printBanner() {
17
18
  function success(msg) {
18
19
  p.log.success(msg);
19
20
  }
20
- function info(msg) {
21
- p.log.info(msg);
22
- }
23
21
  function outro2(msg) {
24
22
  p.outro(pc.green(msg));
25
23
  }
@@ -102,31 +100,38 @@ async function scaffold(projectName, skipInstall) {
102
100
  spinner2.stop("Git initialized");
103
101
  if (!skipInstall) {
104
102
  spinner2.start("Installing dependencies (this may take a minute)");
105
- execSync("npm install", { cwd: targetDir, stdio: "ignore" });
106
- spinner2.stop("Dependencies installed");
107
- info("Running shadcn/ui init...");
108
103
  try {
109
- execSync("npx shadcn@latest init --yes --defaults", {
104
+ execSync("npm install", { cwd: targetDir, stdio: "pipe" });
105
+ spinner2.stop("Dependencies installed");
106
+ } catch (err) {
107
+ spinner2.stop("Failed to install dependencies");
108
+ const stderr = err instanceof Error && "stderr" in err ? err.stderr?.toString() : String(err);
109
+ p3.log.error(stderr || "npm install failed");
110
+ p3.log.info("You can install manually: cd " + projectName + " && npm install");
111
+ }
112
+ spinner2.start("Initializing shadcn/ui");
113
+ try {
114
+ execSync("npx shadcn@latest init --yes --defaults --force --silent", {
110
115
  cwd: targetDir,
111
- stdio: "inherit"
116
+ stdio: "pipe"
112
117
  });
113
- success("shadcn/ui initialized");
118
+ spinner2.stop("shadcn/ui initialized");
114
119
  } catch {
115
- p3.log.warn("shadcn/ui init failed \u2014 run manually: npx shadcn@latest init");
120
+ spinner2.stop("shadcn/ui init failed \u2014 run manually: npx shadcn@latest init");
116
121
  }
117
- info("Adding shadcn/ui base components...");
118
122
  const components = ["button", "input", "card", "form", "label"];
119
123
  for (const component of components) {
124
+ spinner2.start(`Adding shadcn/ui component: ${component}`);
120
125
  try {
121
- execSync(`npx shadcn@latest add ${component} --yes`, {
126
+ execSync(`npx shadcn@latest add ${component} --yes --silent`, {
122
127
  cwd: targetDir,
123
- stdio: "ignore"
128
+ stdio: "pipe"
124
129
  });
130
+ spinner2.stop(`Added shadcn/ui component: ${component}`);
125
131
  } catch {
126
- p3.log.warn(`Failed to add component: ${component}`);
132
+ spinner2.stop(`Failed to add component: ${component}`);
127
133
  }
128
134
  }
129
- success("shadcn/ui components added");
130
135
  }
131
136
  success(pc2.bold(`Project "${projectName}" created!`));
132
137
  console.log(`
@@ -145,8 +150,13 @@ async function scaffold(projectName, skipInstall) {
145
150
  // src/index.ts
146
151
  var program = new Command();
147
152
  program.name("nextjs-hackathon-stack").description("Scaffold a full-stack Next.js 15 hackathon starter").version("0.1.0").argument("[project-name]", "Name of the project to create").option("--skip-install", "Skip npm install and shadcn/ui init", false).action(async (projectName, opts) => {
148
- const options = await runCli(projectName, opts.skipInstall);
149
- await scaffold(options.projectName, options.skipInstall);
150
- outro2("Happy hacking! \u{1F680}");
153
+ try {
154
+ const options = await runCli(projectName, opts.skipInstall);
155
+ await scaffold(options.projectName, options.skipInstall);
156
+ outro2("Happy hacking! \u{1F680}");
157
+ } catch (err) {
158
+ p4.cancel(err instanceof Error ? err.message : "Something went wrong");
159
+ process.exit(1);
160
+ }
151
161
  });
152
162
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextjs-hackathon-stack",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Scaffold a full-stack Next.js hackathon starter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,11 +21,11 @@
21
21
  "dependencies": {
22
22
  "next": "^15",
23
23
  "@supabase/supabase-js": "^2",
24
- "@supabase/ssr": "^0",
25
- "drizzle-orm": "^0.45",
24
+ "@supabase/ssr": "^0.6",
25
+ "drizzle-orm": "^0.44",
26
26
  "drizzle-zod": "^0.7",
27
27
  "@tanstack/react-query": "^5",
28
- "ai": "^4",
28
+ "ai": "^4.3",
29
29
  "@ai-sdk/react": "^1",
30
30
  "react-hook-form": "^7",
31
31
  "@hookform/resolvers": "^3",
@@ -40,11 +40,11 @@
40
40
  "@types/react-dom": "^19",
41
41
  "drizzle-kit": "^0.30",
42
42
  "vitest": "^3",
43
- "@vitejs/plugin-react": "latest",
43
+ "@vitejs/plugin-react": "^4",
44
44
  "@testing-library/react": "^16",
45
- "@testing-library/jest-dom": "latest",
46
- "@testing-library/user-event": "latest",
47
- "@vitest/coverage-v8": "latest",
45
+ "@testing-library/jest-dom": "^6",
46
+ "@testing-library/user-event": "^14",
47
+ "@vitest/coverage-v8": "^3",
48
48
  "@playwright/test": "^1.49",
49
49
  "tailwindcss": "^4",
50
50
  "@tailwindcss/postcss": "^4",
@@ -53,10 +53,10 @@
53
53
  "@eslint/js": "^9",
54
54
  "typescript-eslint": "^8",
55
55
  "@next/eslint-plugin-next": "^15",
56
- "eslint-plugin-react": "latest",
57
- "eslint-plugin-react-hooks": "latest",
58
- "eslint-plugin-import-x": "latest",
59
- "eslint-plugin-vitest": "latest",
60
- "eslint-plugin-playwright": "latest"
56
+ "eslint-plugin-react": "^7",
57
+ "eslint-plugin-react-hooks": "^5",
58
+ "eslint-plugin-import-x": "^4",
59
+ "eslint-plugin-vitest": "^0.5",
60
+ "eslint-plugin-playwright": "^2"
61
61
  }
62
62
  }
@@ -2,9 +2,14 @@ import { LoginForm } from "@/features/auth/components/login-form";
2
2
 
3
3
  export default function LoginPage() {
4
4
  return (
5
- <main className="flex min-h-screen items-center justify-center">
6
- <div className="w-full max-w-md p-8">
7
- <h1 className="mb-8 text-2xl font-bold">Sign in</h1>
5
+ <main className="flex min-h-screen items-center justify-center bg-muted/40 px-4">
6
+ <div className="w-full max-w-sm space-y-6">
7
+ <div className="text-center">
8
+ <h1 className="text-3xl font-bold tracking-tight">Welcome back</h1>
9
+ <p className="mt-2 text-sm text-muted-foreground">
10
+ Sign in to your account to continue
11
+ </p>
12
+ </div>
8
13
  <LoginForm />
9
14
  </div>
10
15
  </main>
@@ -5,6 +5,10 @@ import { useForm } from "react-hook-form";
5
5
  import { zodResolver } from "@hookform/resolvers/zod";
6
6
  import { z } from "zod";
7
7
  import { loginAction, type LoginActionState } from "../actions/login.action";
8
+ import { Button } from "@/components/ui/button";
9
+ import { Input } from "@/components/ui/input";
10
+ import { Label } from "@/components/ui/label";
11
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
8
12
 
9
13
  const schema = z.object({
10
14
  email: z.string().email("Invalid email"),
@@ -32,49 +36,52 @@ export function LoginForm() {
32
36
  });
33
37
 
34
38
  return (
35
- <form onSubmit={onSubmit} data-testid="login-form" className="space-y-4">
36
- {state.status === "error" && (
37
- <p role="alert" className="text-sm text-red-600">
38
- {state.message}
39
- </p>
40
- )}
41
- <div>
42
- <label htmlFor="email" className="block text-sm font-medium">
43
- Email
44
- </label>
45
- <input
46
- id="email"
47
- type="email"
48
- autoComplete="email"
49
- {...register("email")}
50
- className="mt-1 block w-full rounded border px-3 py-2"
51
- />
52
- {errors.email && (
53
- <p className="mt-1 text-sm text-red-600">{errors.email.message}</p>
54
- )}
55
- </div>
56
- <div>
57
- <label htmlFor="password" className="block text-sm font-medium">
58
- Password
59
- </label>
60
- <input
61
- id="password"
62
- type="password"
63
- autoComplete="current-password"
64
- {...register("password")}
65
- className="mt-1 block w-full rounded border px-3 py-2"
66
- />
67
- {errors.password && (
68
- <p className="mt-1 text-sm text-red-600">{errors.password.message}</p>
69
- )}
70
- </div>
71
- <button
72
- type="submit"
73
- disabled={isPending}
74
- className="w-full rounded bg-primary px-4 py-2 text-primary-foreground disabled:opacity-50"
75
- >
76
- {isPending ? "Signing in..." : "Sign in"}
77
- </button>
78
- </form>
39
+ <Card>
40
+ <CardHeader>
41
+ <CardTitle>Sign in</CardTitle>
42
+ <CardDescription>Enter your email and password below</CardDescription>
43
+ </CardHeader>
44
+ <CardContent>
45
+ <form onSubmit={onSubmit} data-testid="login-form" className="space-y-4">
46
+ {state.status === "error" && (
47
+ <p role="alert" className="rounded-md bg-destructive/10 px-3 py-2 text-sm text-destructive">
48
+ {state.message}
49
+ </p>
50
+ )}
51
+
52
+ <div className="space-y-1.5">
53
+ <Label htmlFor="email">Email</Label>
54
+ <Input
55
+ id="email"
56
+ type="email"
57
+ autoComplete="email"
58
+ placeholder="you@example.com"
59
+ {...register("email")}
60
+ />
61
+ {errors.email && (
62
+ <p className="text-sm text-destructive">{errors.email.message}</p>
63
+ )}
64
+ </div>
65
+
66
+ <div className="space-y-1.5">
67
+ <Label htmlFor="password">Password</Label>
68
+ <Input
69
+ id="password"
70
+ type="password"
71
+ autoComplete="current-password"
72
+ placeholder="••••••••"
73
+ {...register("password")}
74
+ />
75
+ {errors.password && (
76
+ <p className="text-sm text-destructive">{errors.password.message}</p>
77
+ )}
78
+ </div>
79
+
80
+ <Button type="submit" className="w-full" disabled={isPending}>
81
+ {isPending ? "Signing in…" : "Sign in"}
82
+ </Button>
83
+ </form>
84
+ </CardContent>
85
+ </Card>
79
86
  );
80
87
  }