create-ncf 0.1.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.
Files changed (44) hide show
  1. package/README.md +145 -0
  2. package/dist/index.js +813 -0
  3. package/dist/index.js.map +1 -0
  4. package/package.json +28 -0
  5. package/templates/auth/src/app/(auth)/sign-in/page.tsx +99 -0
  6. package/templates/auth/src/app/(auth)/sign-up/page.tsx +118 -0
  7. package/templates/auth/src/app/api/auth/[...all]/route.ts +11 -0
  8. package/templates/auth/src/middleware.ts +74 -0
  9. package/templates/auth/src/server/auth/auth-client.ts +8 -0
  10. package/templates/auth/src/server/auth/auth.ts +77 -0
  11. package/templates/auth/src/server/db/schema/auth.ts +115 -0
  12. package/templates/base/biome.jsonc +68 -0
  13. package/templates/base/open-next.config.ts +3 -0
  14. package/templates/base/postcss.config.mjs +5 -0
  15. package/templates/base/src/app/layout.tsx +30 -0
  16. package/templates/base/src/app/page.tsx +24 -0
  17. package/templates/base/src/app/robots.ts +13 -0
  18. package/templates/base/src/app/sitemap.ts +12 -0
  19. package/templates/base/src/lib/utils.ts +6 -0
  20. package/templates/base/src/middleware.ts +9 -0
  21. package/templates/base/src/styles/globals.css +121 -0
  22. package/templates/base/tsconfig.json +37 -0
  23. package/templates/drizzle/drizzle.config.ts +7 -0
  24. package/templates/drizzle/migrations/.gitkeep +0 -0
  25. package/templates/drizzle/src/server/db/index.ts +9 -0
  26. package/templates/drizzle/src/server/db/schema/example.ts +15 -0
  27. package/templates/drizzle/src/server/db/schema/index.ts +1 -0
  28. package/templates/image-loader/src/lib/image-loader.ts +34 -0
  29. package/templates/posthog/instrumentation-client.ts +15 -0
  30. package/templates/queues/src/server/queues/handler.ts +43 -0
  31. package/templates/r2/src/server/services/storage.ts +53 -0
  32. package/templates/shadcn/components.json +22 -0
  33. package/templates/shadcn/src/components/ui/button.tsx +59 -0
  34. package/templates/shadcn/src/components/ui/card.tsx +92 -0
  35. package/templates/shadcn/src/components/ui/input.tsx +21 -0
  36. package/templates/shadcn/src/components/ui/skeleton.tsx +13 -0
  37. package/templates/shadcn/src/components/ui/sonner.tsx +28 -0
  38. package/templates/trpc/src/app/api/trpc/[trpc]/route.ts +29 -0
  39. package/templates/trpc/src/server/api/root.ts +10 -0
  40. package/templates/trpc/src/server/api/routes/example.ts +12 -0
  41. package/templates/trpc/src/server/api/trpc.ts +45 -0
  42. package/templates/trpc/src/server/api/trpc.with-auth.ts +67 -0
  43. package/templates/trpc/src/trpc/query-client.ts +23 -0
  44. package/templates/trpc/src/trpc/react.tsx +65 -0
@@ -0,0 +1,92 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "~/lib/utils"
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
+ className
12
+ )}
13
+ {...props}
14
+ />
15
+ )
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ )
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ )
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className
58
+ )}
59
+ {...props}
60
+ />
61
+ )
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-6", className)}
69
+ {...props}
70
+ />
71
+ )
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79
+ {...props}
80
+ />
81
+ )
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ }
@@ -0,0 +1,21 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "~/lib/utils"
4
+
5
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
+ return (
7
+ <input
8
+ type={type}
9
+ data-slot="input"
10
+ className={cn(
11
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
12
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
13
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
14
+ className
15
+ )}
16
+ {...props}
17
+ />
18
+ )
19
+ }
20
+
21
+ export { Input }
@@ -0,0 +1,13 @@
1
+ import { cn } from "~/lib/utils"
2
+
3
+ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
4
+ return (
5
+ <div
6
+ data-slot="skeleton"
7
+ className={cn("bg-primary/10 animate-pulse rounded-md", className)}
8
+ {...props}
9
+ />
10
+ )
11
+ }
12
+
13
+ export { Skeleton }
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+ import { Toaster as Sonner } from "sonner";
5
+
6
+ type ToasterProps = ComponentProps<typeof Sonner>;
7
+
8
+ function Toaster({ ...props }: ToasterProps) {
9
+ return (
10
+ <Sonner
11
+ className="toaster group"
12
+ toastOptions={{
13
+ classNames: {
14
+ toast:
15
+ "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
16
+ description: "group-[.toast]:text-muted-foreground",
17
+ actionButton:
18
+ "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
19
+ cancelButton:
20
+ "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
21
+ },
22
+ }}
23
+ {...props}
24
+ />
25
+ );
26
+ }
27
+
28
+ export { Toaster };
@@ -0,0 +1,29 @@
1
+ import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
2
+ import type { NextRequest } from "next/server";
3
+
4
+ import { appRouter } from "~/server/api/root";
5
+ import { createTRPCContext } from "~/server/api/trpc";
6
+
7
+ const createContext = async (req: NextRequest) => {
8
+ return createTRPCContext({
9
+ headers: req.headers,
10
+ });
11
+ };
12
+
13
+ const handler = (req: NextRequest) =>
14
+ fetchRequestHandler({
15
+ endpoint: "/api/trpc",
16
+ req,
17
+ router: appRouter,
18
+ createContext: () => createContext(req),
19
+ onError:
20
+ process.env.NODE_ENV === "development"
21
+ ? ({ path, error }) => {
22
+ console.error(
23
+ `tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
24
+ );
25
+ }
26
+ : undefined,
27
+ });
28
+
29
+ export { handler as GET, handler as POST };
@@ -0,0 +1,10 @@
1
+ import { createCallerFactory, createTRPCRouter } from "~/server/api/trpc";
2
+ import { exampleRouter } from "~/server/api/routes/example";
3
+
4
+ export const appRouter = createTRPCRouter({
5
+ example: exampleRouter,
6
+ });
7
+
8
+ export type AppRouter = typeof appRouter;
9
+
10
+ export const createCaller = createCallerFactory(appRouter);
@@ -0,0 +1,12 @@
1
+ import { z } from "zod";
2
+ import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
3
+
4
+ export const exampleRouter = createTRPCRouter({
5
+ hello: publicProcedure
6
+ .input(z.object({ text: z.string() }))
7
+ .query(({ input }) => {
8
+ return {
9
+ greeting: `Hello ${input.text}`,
10
+ };
11
+ }),
12
+ });
@@ -0,0 +1,45 @@
1
+ import { initTRPC } from "@trpc/server";
2
+ import superjson from "superjson";
3
+ import { ZodError } from "zod";
4
+
5
+ export const createTRPCContext = async (opts: { headers: Headers }) => {
6
+ return {
7
+ ...opts,
8
+ };
9
+ };
10
+
11
+ const t = initTRPC.context<typeof createTRPCContext>().create({
12
+ transformer: superjson,
13
+ errorFormatter({ shape, error }) {
14
+ return {
15
+ ...shape,
16
+ data: {
17
+ ...shape.data,
18
+ zodError:
19
+ error.cause instanceof ZodError ? error.cause.flatten() : null,
20
+ },
21
+ };
22
+ },
23
+ });
24
+
25
+ export const createCallerFactory = t.createCallerFactory;
26
+
27
+ export const createTRPCRouter = t.router;
28
+
29
+ const timingMiddleware = t.middleware(async ({ next, path }) => {
30
+ const start = Date.now();
31
+
32
+ if (t._config.isDev) {
33
+ const waitMs = Math.floor(Math.random() * 400) + 100;
34
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
35
+ }
36
+
37
+ const result = await next();
38
+
39
+ const end = Date.now();
40
+ console.log(`[TRPC] ${path} took ${end - start}ms to execute`);
41
+
42
+ return result;
43
+ });
44
+
45
+ export const publicProcedure = t.procedure.use(timingMiddleware);
@@ -0,0 +1,67 @@
1
+ import { initTRPC, TRPCError } from "@trpc/server";
2
+ import superjson from "superjson";
3
+ import { ZodError } from "zod";
4
+ import { initAuth } from "~/server/auth/auth";
5
+ import { getDB } from "~/server/db";
6
+
7
+ export const createTRPCContext = async (opts: { headers: Headers }) => {
8
+ const auth = await initAuth();
9
+ const session = await auth.api.getSession({
10
+ headers: opts.headers,
11
+ });
12
+
13
+ return {
14
+ db: await getDB(),
15
+ session,
16
+ ...opts,
17
+ };
18
+ };
19
+
20
+ const t = initTRPC.context<typeof createTRPCContext>().create({
21
+ transformer: superjson,
22
+ errorFormatter({ shape, error }) {
23
+ return {
24
+ ...shape,
25
+ data: {
26
+ ...shape.data,
27
+ zodError:
28
+ error.cause instanceof ZodError ? error.cause.flatten() : null,
29
+ },
30
+ };
31
+ },
32
+ });
33
+
34
+ export const createCallerFactory = t.createCallerFactory;
35
+
36
+ export const createTRPCRouter = t.router;
37
+
38
+ const timingMiddleware = t.middleware(async ({ next, path }) => {
39
+ const start = Date.now();
40
+
41
+ if (t._config.isDev) {
42
+ const waitMs = Math.floor(Math.random() * 400) + 100;
43
+ await new Promise((resolve) => setTimeout(resolve, waitMs));
44
+ }
45
+
46
+ const result = await next();
47
+
48
+ const end = Date.now();
49
+ console.log(`[TRPC] ${path} took ${end - start}ms to execute`);
50
+
51
+ return result;
52
+ });
53
+
54
+ export const publicProcedure = t.procedure.use(timingMiddleware);
55
+
56
+ export const protectedProcedure = publicProcedure.use(async ({ ctx, next }) => {
57
+ if (!ctx.session?.user) {
58
+ throw new TRPCError({ code: "UNAUTHORIZED" });
59
+ }
60
+
61
+ return next({
62
+ ctx: {
63
+ ...ctx,
64
+ session: ctx.session,
65
+ },
66
+ });
67
+ });
@@ -0,0 +1,23 @@
1
+ import {
2
+ defaultShouldDehydrateQuery,
3
+ QueryClient,
4
+ } from "@tanstack/react-query";
5
+ import SuperJSON from "superjson";
6
+
7
+ export const createQueryClient = () =>
8
+ new QueryClient({
9
+ defaultOptions: {
10
+ queries: {
11
+ staleTime: 30 * 1000,
12
+ },
13
+ dehydrate: {
14
+ serializeData: SuperJSON.serialize,
15
+ shouldDehydrateQuery: (query) =>
16
+ defaultShouldDehydrateQuery(query) ||
17
+ query.state.status === "pending",
18
+ },
19
+ hydrate: {
20
+ deserializeData: SuperJSON.deserialize,
21
+ },
22
+ },
23
+ });
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import { type QueryClient, QueryClientProvider } from "@tanstack/react-query";
4
+ import { httpBatchStreamLink, loggerLink } from "@trpc/client";
5
+ import { createTRPCReact } from "@trpc/react-query";
6
+ import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server";
7
+ import { useState } from "react";
8
+ import SuperJSON from "superjson";
9
+
10
+ import type { AppRouter } from "~/server/api/root";
11
+ import { createQueryClient } from "./query-client";
12
+
13
+ let clientQueryClientSingleton: QueryClient | undefined = undefined;
14
+ const getQueryClient = () => {
15
+ if (typeof window === "undefined") {
16
+ return createQueryClient();
17
+ }
18
+ clientQueryClientSingleton ??= createQueryClient();
19
+
20
+ return clientQueryClientSingleton;
21
+ };
22
+
23
+ export const api = createTRPCReact<AppRouter>();
24
+
25
+ export type RouterInputs = inferRouterInputs<AppRouter>;
26
+ export type RouterOutputs = inferRouterOutputs<AppRouter>;
27
+
28
+ export function TRPCReactProvider(props: { children: React.ReactNode }) {
29
+ const queryClient = getQueryClient();
30
+
31
+ const [trpcClient] = useState(() =>
32
+ api.createClient({
33
+ links: [
34
+ loggerLink({
35
+ enabled: (op) =>
36
+ process.env.NODE_ENV === "development" ||
37
+ (op.direction === "down" && op.result instanceof Error),
38
+ }),
39
+ httpBatchStreamLink({
40
+ transformer: SuperJSON,
41
+ url: `${getBaseUrl()}/api/trpc`,
42
+ headers: () => {
43
+ const headers = new Headers();
44
+ headers.set("x-trpc-source", "nextjs-react");
45
+ return headers;
46
+ },
47
+ }),
48
+ ],
49
+ }),
50
+ );
51
+
52
+ return (
53
+ <QueryClientProvider client={queryClient}>
54
+ <api.Provider client={trpcClient} queryClient={queryClient}>
55
+ {props.children}
56
+ </api.Provider>
57
+ </QueryClientProvider>
58
+ );
59
+ }
60
+
61
+ function getBaseUrl() {
62
+ if (typeof window !== "undefined") return window.location.origin;
63
+ if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`;
64
+ return `http://localhost:${process.env.PORT ?? 3000}`;
65
+ }