better-ts-stack 2.0.2 → 2.1.1

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.
Files changed (68) hide show
  1. package/README.md +114 -113
  2. package/dist/args.js +27 -20
  3. package/dist/builder/buildPlanner.js +1 -1
  4. package/dist/builder/configGenerator.js +4 -6
  5. package/dist/builder/index.js +15 -3
  6. package/dist/builder/moduleSelector.js +6 -2
  7. package/dist/builder/readmeGenerator.js +107 -0
  8. package/dist/builder/templateContext.js +53 -0
  9. package/dist/index.js +7 -1
  10. package/dist/output/nextSteps.js +23 -22
  11. package/dist/prompts/index.js +5 -2
  12. package/dist/prompts/stack.js +23 -2
  13. package/dist/types/index.js +2 -0
  14. package/dist/ui/messages.js +3 -1
  15. package/dist/validators/index.js +5 -2
  16. package/package.json +3 -3
  17. package/templates/backend/express/config.json +42 -40
  18. package/templates/backend/express/gitignore +3 -0
  19. package/templates/backend/express/src/env.ts.hbs +39 -0
  20. package/templates/backend/express/src/index.ts.hbs +111 -75
  21. package/templates/backend/nestjs/config.json +43 -41
  22. package/templates/backend/nestjs/gitignore +3 -0
  23. package/templates/backend/nestjs/src/app.module.ts.hbs +13 -0
  24. package/templates/backend/nestjs/src/env.ts.hbs +39 -0
  25. package/templates/backend/nestjs/src/health/{health.controller.ts → health.controller.ts.hbs} +8 -0
  26. package/templates/backend/nestjs/src/main.ts +3 -2
  27. package/templates/frontend/nextjs/app/page.tsx.hbs +98 -97
  28. package/templates/frontend/nextjs/config.json +12 -14
  29. package/templates/frontend/nextjs/gitignore +3 -0
  30. package/templates/frontend/tanstack/config.json +16 -16
  31. package/templates/frontend/tanstack/gitignore +3 -0
  32. package/templates/frontend/tanstack/src/routes/index.tsx.hbs +102 -101
  33. package/templates/frontend/tanstack/vite.config.ts +23 -14
  34. package/templates/modules/auth/express/config.json +20 -20
  35. package/templates/modules/auth/express/src/middleware/requireAuth.ts.hbs +1 -2
  36. package/templates/modules/auth/express/src/routes/auth.ts.hbs +27 -19
  37. package/templates/modules/auth/nestjs/config.json +20 -20
  38. package/templates/modules/auth/nestjs/src/auth/{auth.controller.ts → auth.controller.ts.hbs} +15 -0
  39. package/templates/modules/auth/nextjs/config.json +4 -4
  40. package/templates/{frontend/nextjs/proxy.ts.hbs → modules/auth/nextjs/proxy.ts} +1 -8
  41. package/templates/modules/auth/tanstack/config.json +4 -4
  42. package/templates/modules/auth/tanstack/src/components/auth/sign-in-form.tsx +132 -134
  43. package/templates/modules/auth/tanstack/src/components/auth/sign-out-button.tsx +46 -48
  44. package/templates/modules/auth/tanstack/src/components/auth/sign-up-form.tsx +150 -152
  45. package/templates/modules/ci/.github/workflows/ci.yml.hbs +43 -0
  46. package/templates/modules/ci/config.json +10 -0
  47. package/templates/modules/docker/.dockerignore +2 -0
  48. package/templates/modules/docker/Dockerfile.hbs +147 -136
  49. package/templates/modules/docker/docker-compose.yml.hbs +97 -2
  50. package/templates/modules/drizzle/express/config.json +3 -3
  51. package/templates/modules/drizzle/nestjs/config.json +3 -3
  52. package/templates/modules/drizzle/nextjs/config.json +3 -3
  53. package/templates/modules/drizzle/tanstack/config.json +3 -3
  54. package/templates/modules/mongoose/express/config.json +2 -2
  55. package/templates/modules/mongoose/nestjs/config.json +1 -1
  56. package/templates/modules/mongoose/nextjs/config.json +2 -2
  57. package/templates/modules/mongoose/tanstack/config.json +2 -2
  58. package/templates/modules/prisma/express/config.json +4 -4
  59. package/templates/modules/prisma/express/prisma.config.ts +4 -2
  60. package/templates/modules/prisma/nestjs/config.json +4 -4
  61. package/templates/modules/prisma/nestjs/prisma.config.ts +4 -2
  62. package/templates/modules/prisma/nextjs/config.json +4 -4
  63. package/templates/modules/prisma/nextjs/prisma.config.ts +4 -2
  64. package/templates/modules/prisma/tanstack/config.json +4 -4
  65. package/templates/modules/prisma/tanstack/prisma.config.ts +4 -2
  66. package/templates/modules/rate-limit/express/config.json +12 -0
  67. package/templates/modules/rate-limit/express/src/middleware/rateLimit.ts +23 -0
  68. package/templates/modules/rate-limit/nestjs/config.json +12 -0
@@ -1,135 +1,133 @@
1
- "use client";
2
-
3
- import { Link, useNavigate } from "@tanstack/react-router";
4
- import { useState, useTransition } from "react";
5
- import { zodResolver } from "@hookform/resolvers/zod";
6
- import { LoaderCircle } from "lucide-react";
7
- import { useForm } from "react-hook-form";
8
-
9
- import { authClient } from "~/lib/auth-client";
10
- import {
11
- getAuthErrorMessage,
12
- signInSchema,
13
- type SignInValues,
14
- } from "~/lib/auth-schema";
15
- import { Button } from "~/components/ui/button";
16
- import {
17
- Card,
18
- CardContent,
19
- CardDescription,
20
- CardFooter,
21
- CardHeader,
22
- CardTitle,
23
- } from "~/components/ui/card";
24
- import {
25
- Field,
26
- FieldDescription,
27
- FieldError,
28
- FieldGroup,
29
- FieldLabel,
30
- } from "~/components/ui/field";
31
- import { Input } from "~/components/ui/input";
32
-
33
- export function SignInForm() {
34
- const navigate = useNavigate();
35
- const [isPending, startTransition] = useTransition();
36
- const [authError, setAuthError] = useState<string | null>(null);
37
- const form = useForm<SignInValues>({
38
- resolver: zodResolver(signInSchema),
39
- defaultValues: {
40
- email: "",
41
- password: "",
42
- },
43
- });
44
-
45
- const onSubmit = form.handleSubmit((values) => {
46
- setAuthError(null);
47
-
48
- startTransition(async () => {
49
- const result = await authClient.signIn.email({
50
- email: values.email,
51
- password: values.password,
52
- });
53
-
54
- if (result.error) {
55
- setAuthError(getAuthErrorMessage(result.error));
56
- return;
57
- }
58
-
59
- navigate({ to: "/dashboard" });
60
- });
61
- });
62
-
63
- return (
64
- <Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
65
- <CardHeader className="space-y-2">
66
- <CardTitle>Login to your account</CardTitle>
67
- <CardDescription>
68
- Sign in with your email and password to continue to your dashboard.
69
- </CardDescription>
70
- </CardHeader>
71
- <CardContent>
72
- <form className="space-y-6" onSubmit={onSubmit} noValidate>
73
- <FieldGroup>
74
- <Field data-invalid={Boolean(form.formState.errors.email)}>
75
- <FieldLabel htmlFor="email">Email</FieldLabel>
76
- <Input
77
- id="email"
78
- type="email"
79
- autoComplete="email"
80
- aria-invalid={Boolean(form.formState.errors.email)}
81
- placeholder="you@example.com"
82
- {...form.register("email")}
83
- />
84
- <FieldDescription>
85
- Use the email you registered with.
86
- </FieldDescription>
87
- <FieldError errors={[form.formState.errors.email]} />
88
- </Field>
89
-
90
- <Field data-invalid={Boolean(form.formState.errors.password)}>
91
- <FieldLabel htmlFor="password">Password</FieldLabel>
92
- <Input
93
- id="password"
94
- type="password"
95
- autoComplete="current-password"
96
- aria-invalid={Boolean(form.formState.errors.password)}
97
- placeholder="Enter your password"
98
- {...form.register("password")}
99
- />
100
- <FieldError errors={[form.formState.errors.password]} />
101
- </Field>
102
- </FieldGroup>
103
-
104
- {authError ? (
105
- <div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
106
- {authError}
107
- </div>
108
- ) : null}
109
-
110
- <Button className="w-full" type="submit" disabled={isPending}>
111
- {isPending ? (
112
- <>
113
- <LoaderCircle className="mr-2 size-4 animate-spin" />
114
- Signing in
115
- </>
116
- ) : (
117
- "Login"
118
- )}
119
- </Button>
120
- </form>
121
- </CardContent>
122
- <CardFooter className="justify-center">
123
- <p className="text-sm text-slate-600">
124
- Need an account?{" "}
125
- <Link
126
- className="font-medium text-sky-700 hover:text-sky-800"
127
- to="/sign-up"
128
- >
129
- Signup
130
- </Link>
131
- </p>
132
- </CardFooter>
133
- </Card>
134
- );
1
+ import { Link, useNavigate } from "@tanstack/react-router";
2
+ import { useState, useTransition } from "react";
3
+ import { zodResolver } from "@hookform/resolvers/zod";
4
+ import { LoaderCircle } from "lucide-react";
5
+ import { useForm } from "react-hook-form";
6
+
7
+ import { authClient } from "~/lib/auth-client";
8
+ import {
9
+ getAuthErrorMessage,
10
+ signInSchema,
11
+ type SignInValues,
12
+ } from "~/lib/auth-schema";
13
+ import { Button } from "~/components/ui/button";
14
+ import {
15
+ Card,
16
+ CardContent,
17
+ CardDescription,
18
+ CardFooter,
19
+ CardHeader,
20
+ CardTitle,
21
+ } from "~/components/ui/card";
22
+ import {
23
+ Field,
24
+ FieldDescription,
25
+ FieldError,
26
+ FieldGroup,
27
+ FieldLabel,
28
+ } from "~/components/ui/field";
29
+ import { Input } from "~/components/ui/input";
30
+
31
+ export function SignInForm() {
32
+ const navigate = useNavigate();
33
+ const [isPending, startTransition] = useTransition();
34
+ const [authError, setAuthError] = useState<string | null>(null);
35
+ const form = useForm<SignInValues>({
36
+ resolver: zodResolver(signInSchema),
37
+ defaultValues: {
38
+ email: "",
39
+ password: "",
40
+ },
41
+ });
42
+
43
+ const onSubmit = form.handleSubmit((values) => {
44
+ setAuthError(null);
45
+
46
+ startTransition(async () => {
47
+ const result = await authClient.signIn.email({
48
+ email: values.email,
49
+ password: values.password,
50
+ });
51
+
52
+ if (result.error) {
53
+ setAuthError(getAuthErrorMessage(result.error));
54
+ return;
55
+ }
56
+
57
+ navigate({ to: "/dashboard" });
58
+ });
59
+ });
60
+
61
+ return (
62
+ <Card className="border-white/80 bg-white/90 shadow-2xl shadow-slate-200/70 backdrop-blur">
63
+ <CardHeader className="space-y-2">
64
+ <CardTitle>Login to your account</CardTitle>
65
+ <CardDescription>
66
+ Sign in with your email and password to continue to your dashboard.
67
+ </CardDescription>
68
+ </CardHeader>
69
+ <CardContent>
70
+ <form className="space-y-6" onSubmit={onSubmit} noValidate>
71
+ <FieldGroup>
72
+ <Field data-invalid={Boolean(form.formState.errors.email)}>
73
+ <FieldLabel htmlFor="email">Email</FieldLabel>
74
+ <Input
75
+ id="email"
76
+ type="email"
77
+ autoComplete="email"
78
+ aria-invalid={Boolean(form.formState.errors.email)}
79
+ placeholder="you@example.com"
80
+ {...form.register("email")}
81
+ />
82
+ <FieldDescription>
83
+ Use the email you registered with.
84
+ </FieldDescription>
85
+ <FieldError errors={[form.formState.errors.email]} />
86
+ </Field>
87
+
88
+ <Field data-invalid={Boolean(form.formState.errors.password)}>
89
+ <FieldLabel htmlFor="password">Password</FieldLabel>
90
+ <Input
91
+ id="password"
92
+ type="password"
93
+ autoComplete="current-password"
94
+ aria-invalid={Boolean(form.formState.errors.password)}
95
+ placeholder="Enter your password"
96
+ {...form.register("password")}
97
+ />
98
+ <FieldError errors={[form.formState.errors.password]} />
99
+ </Field>
100
+ </FieldGroup>
101
+
102
+ {authError ? (
103
+ <div className="rounded-2xl border border-rose-200 bg-rose-50 px-4 py-3 text-sm text-rose-700">
104
+ {authError}
105
+ </div>
106
+ ) : null}
107
+
108
+ <Button className="w-full" type="submit" disabled={isPending}>
109
+ {isPending ? (
110
+ <>
111
+ <LoaderCircle className="mr-2 size-4 animate-spin" />
112
+ Signing in
113
+ </>
114
+ ) : (
115
+ "Login"
116
+ )}
117
+ </Button>
118
+ </form>
119
+ </CardContent>
120
+ <CardFooter className="justify-center">
121
+ <p className="text-sm text-slate-600">
122
+ Need an account?{" "}
123
+ <Link
124
+ className="font-medium text-sky-700 hover:text-sky-800"
125
+ to="/sign-up"
126
+ >
127
+ Signup
128
+ </Link>
129
+ </p>
130
+ </CardFooter>
131
+ </Card>
132
+ );
135
133
  }
@@ -1,49 +1,47 @@
1
- "use client";
2
-
3
- import { useNavigate } from "@tanstack/react-router";
4
- import { useState, useTransition } from "react";
5
- import { LoaderCircle, LogOut } from "lucide-react";
6
-
7
- import { authClient } from "~/lib/auth-client";
8
- import { Button } from "~/components/ui/button";
9
-
10
- export function SignOutButton() {
11
- const navigate = useNavigate();
12
- const [isPending, startTransition] = useTransition();
13
- const [error, setError] = useState<string | null>(null);
14
-
15
- return (
16
- <div className="space-y-2">
17
- <Button
18
- variant="outline"
19
- onClick={() => {
20
- setError(null);
21
- startTransition(async () => {
22
- const result = await authClient.signOut();
23
-
24
- if (result.error) {
25
- setError("Unable to sign out right now. Please try again.");
26
- return;
27
- }
28
-
29
- navigate({ to: "/sign-in" });
30
- });
31
- }}
32
- disabled={isPending}
33
- >
34
- {isPending ? (
35
- <>
36
- <LoaderCircle className="mr-2 size-4 animate-spin" />
37
- Signing out
38
- </>
39
- ) : (
40
- <>
41
- <LogOut className="mr-2 size-4" />
42
- Sign out
43
- </>
44
- )}
45
- </Button>
46
- {error ? <p className="text-sm text-rose-600">{error}</p> : null}
47
- </div>
48
- );
1
+ import { useNavigate } from "@tanstack/react-router";
2
+ import { useState, useTransition } from "react";
3
+ import { LoaderCircle, LogOut } from "lucide-react";
4
+
5
+ import { authClient } from "~/lib/auth-client";
6
+ import { Button } from "~/components/ui/button";
7
+
8
+ export function SignOutButton() {
9
+ const navigate = useNavigate();
10
+ const [isPending, startTransition] = useTransition();
11
+ const [error, setError] = useState<string | null>(null);
12
+
13
+ return (
14
+ <div className="space-y-2">
15
+ <Button
16
+ variant="outline"
17
+ onClick={() => {
18
+ setError(null);
19
+ startTransition(async () => {
20
+ const result = await authClient.signOut();
21
+
22
+ if (result.error) {
23
+ setError("Unable to sign out right now. Please try again.");
24
+ return;
25
+ }
26
+
27
+ navigate({ to: "/sign-in" });
28
+ });
29
+ }}
30
+ disabled={isPending}
31
+ >
32
+ {isPending ? (
33
+ <>
34
+ <LoaderCircle className="mr-2 size-4 animate-spin" />
35
+ Signing out
36
+ </>
37
+ ) : (
38
+ <>
39
+ <LogOut className="mr-2 size-4" />
40
+ Sign out
41
+ </>
42
+ )}
43
+ </Button>
44
+ {error ? <p className="text-sm text-rose-600">{error}</p> : null}
45
+ </div>
46
+ );
49
47
  }