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("
|
|
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: "
|
|
116
|
+
stdio: "pipe"
|
|
112
117
|
});
|
|
113
|
-
|
|
118
|
+
spinner2.stop("shadcn/ui initialized");
|
|
114
119
|
} catch {
|
|
115
|
-
|
|
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: "
|
|
128
|
+
stdio: "pipe"
|
|
124
129
|
});
|
|
130
|
+
spinner2.stop(`Added shadcn/ui component: ${component}`);
|
|
125
131
|
} catch {
|
|
126
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
@@ -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.
|
|
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": "
|
|
43
|
+
"@vitejs/plugin-react": "^4",
|
|
44
44
|
"@testing-library/react": "^16",
|
|
45
|
-
"@testing-library/jest-dom": "
|
|
46
|
-
"@testing-library/user-event": "
|
|
47
|
-
"@vitest/coverage-v8": "
|
|
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": "
|
|
57
|
-
"eslint-plugin-react-hooks": "
|
|
58
|
-
"eslint-plugin-import-x": "
|
|
59
|
-
"eslint-plugin-vitest": "
|
|
60
|
-
"eslint-plugin-playwright": "
|
|
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-
|
|
7
|
-
<
|
|
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
|
-
<
|
|
36
|
-
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
}
|