From bc15ece4dd3ce46bc9f2a8449a1ef86d30c73818 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sat, 16 Jul 2022 22:17:52 +0200 Subject: [PATCH 01/14] feat: update trpc to v10 (@next) (#203) * feat: modify installer and bp * feat: update frontend * fix: update import * fix: add protectedprocedure --- src/installers/trpc.ts | 33 ++++++---- template/addons/trpc/api-handler.ts | 4 +- template/addons/trpc/auth-context.ts | 16 ++--- template/addons/trpc/auth-index-router.ts | 14 ++-- template/addons/trpc/auth-prisma-context.ts | 16 ++--- template/addons/trpc/auth-router.ts | 30 +++------ template/addons/trpc/auth-server-utils.ts | 23 +++++++ template/addons/trpc/base-context.ts | 14 +--- template/addons/trpc/example-prisma-router.ts | 26 +++----- template/addons/trpc/example-router.ts | 21 +++--- template/addons/trpc/index-router.ts | 9 ++- template/addons/trpc/prisma-context.ts | 11 +--- template/addons/trpc/server-utils.ts | 10 +++ template/addons/trpc/utils.ts | 42 ++++++++++-- template/page-studs/_app/with-auth-trpc.tsx | 47 ++------------ template/page-studs/_app/with-trpc.tsx | 39 +---------- .../page-studs/index/with-auth-trpc-tw.tsx | 64 +++++++++---------- template/page-studs/index/with-auth-trpc.tsx | 47 +++++++------- template/page-studs/index/with-trpc-tw.tsx | 24 +++---- template/page-studs/index/with-trpc.tsx | 9 ++- template/page-studs/index/with-tw.tsx | 20 +++--- 21 files changed, 234 insertions(+), 285 deletions(-) create mode 100644 template/addons/trpc/auth-server-utils.ts create mode 100644 template/addons/trpc/server-utils.ts diff --git a/src/installers/trpc.ts b/src/installers/trpc.ts index 7c6d4ca0ee..cd76b6da80 100644 --- a/src/installers/trpc.ts +++ b/src/installers/trpc.ts @@ -16,10 +16,10 @@ export const trpcInstaller: Installer = async ({ packages: [ "react-query", "superjson", - "@trpc/server", - "@trpc/client", - "@trpc/next", - "@trpc/react", + "@trpc/server@experimental", + "@trpc/client@experimental", + "@trpc/next@experimental", + "@trpc/react@experimental", ], devMode: false, noInstallMode: noInstall, @@ -35,6 +35,10 @@ export const trpcInstaller: Installer = async ({ const utilsSrc = path.join(trpcAssetDir, "utils.ts"); const utilsDest = path.join(projectDir, "src/utils/trpc.ts"); + const serverUtilFile = usingAuth ? "auth-server-utils.ts" : "server-utils.ts"; + const serverUtilSrc = path.join(trpcAssetDir, serverUtilFile); + const serverUtilDest = path.join(projectDir, "src/server/trpc/utils.ts"); + const contextFile = usingAuth && usingPrisma ? "auth-prisma-context.ts" @@ -44,19 +48,22 @@ export const trpcInstaller: Installer = async ({ ? "prisma-context.ts" : "base-context.ts"; const contextSrc = path.join(trpcAssetDir, contextFile); - const contextDest = path.join(projectDir, "src/server/router/context.ts"); + const contextDest = path.join(projectDir, "src/server/trpc/context.ts"); - if (usingAuth) { - const authRouterSrc = path.join(trpcAssetDir, "auth-router.ts"); - const authRouterDest = path.join(projectDir, "src/server/router/auth.ts"); - await fs.copy(authRouterSrc, authRouterDest); - } + const authRouterSrc = path.join(trpcAssetDir, "auth-router.ts"); + const authRouterDest = path.join( + projectDir, + "src/server/trpc/router/auth.ts", + ); const indexRouterFile = usingAuth ? "auth-index-router.ts" : "index-router.ts"; const indexRouterSrc = path.join(trpcAssetDir, indexRouterFile); - const indexRouterDest = path.join(projectDir, "src/server/router/index.ts"); + const indexRouterDest = path.join( + projectDir, + "src/server/trpc/router/index.ts", + ); const exampleRouterFile = usingPrisma ? "example-prisma-router.ts" @@ -64,14 +71,16 @@ export const trpcInstaller: Installer = async ({ const exampleRouterSrc = path.join(trpcAssetDir, exampleRouterFile); const exampleRouterDest = path.join( projectDir, - "src/server/router/example.ts", + "src/server/trpc/router/example.ts", ); await Promise.all([ fs.copy(apiHandlerSrc, apiHandlerDest), fs.copy(utilsSrc, utilsDest), + fs.copy(serverUtilSrc, serverUtilDest), fs.copy(contextSrc, contextDest), fs.copy(indexRouterSrc, indexRouterDest), fs.copy(exampleRouterSrc, exampleRouterDest), + ...(usingAuth ? [fs.copy(authRouterSrc, authRouterDest)] : []), ]); }; diff --git a/template/addons/trpc/api-handler.ts b/template/addons/trpc/api-handler.ts index b760961164..6e4f4911bc 100644 --- a/template/addons/trpc/api-handler.ts +++ b/template/addons/trpc/api-handler.ts @@ -1,7 +1,7 @@ // src/pages/api/trpc/[trpc].ts import { createNextApiHandler } from "@trpc/server/adapters/next"; -import { appRouter } from "../../../server/router"; -import { createContext } from "../../../server/router/context"; +import { appRouter } from "../../../server/trpc/router"; +import { createContext } from "../../../server/trpc/context"; // export API handler export default createNextApiHandler({ diff --git a/template/addons/trpc/auth-context.ts b/template/addons/trpc/auth-context.ts index 5cf58ece54..f6cfbaabb1 100644 --- a/template/addons/trpc/auth-context.ts +++ b/template/addons/trpc/auth-context.ts @@ -1,4 +1,4 @@ -// src/server/router/context.ts +// src/server/trpc/context.ts import * as trpc from "@trpc/server"; import * as trpcNext from "@trpc/server/adapters/next"; import { unstable_getServerSession as getServerSession } from "next-auth"; @@ -6,21 +6,13 @@ import { unstable_getServerSession as getServerSession } from "next-auth"; import { authOptions as nextAuthOptions } from "../../pages/api/auth/[...nextauth]"; export const createContext = async ( - opts?: trpcNext.CreateNextContextOptions, + opts: trpcNext.CreateNextContextOptions, ) => { - const req = opts?.req; - const res = opts?.res; - - const session = - req && res && (await getServerSession(req, res, nextAuthOptions)); + const session = await getServerSession(opts.req, opts.res, nextAuthOptions); return { - req, - res, session, }; }; -type Context = trpc.inferAsyncReturnType; - -export const createRouter = () => trpc.router(); +export type Context = trpc.inferAsyncReturnType; diff --git a/template/addons/trpc/auth-index-router.ts b/template/addons/trpc/auth-index-router.ts index 5c83f8c125..14ba1c1b4e 100644 --- a/template/addons/trpc/auth-index-router.ts +++ b/template/addons/trpc/auth-index-router.ts @@ -1,14 +1,12 @@ -// src/server/router/index.ts -import { createRouter } from "./context"; -import superjson from "superjson"; - +// src/server/trpc/router/index.ts +import { t } from "../utils"; import { exampleRouter } from "./example"; import { authRouter } from "./auth"; -export const appRouter = createRouter() - .transformer(superjson) - .merge("example.", exampleRouter) - .merge("auth.", authRouter); +export const appRouter = t.router({ + example: exampleRouter, + auth: authRouter, +}); // export type definition of API export type AppRouter = typeof appRouter; diff --git a/template/addons/trpc/auth-prisma-context.ts b/template/addons/trpc/auth-prisma-context.ts index 10fc485d47..c1dc601355 100644 --- a/template/addons/trpc/auth-prisma-context.ts +++ b/template/addons/trpc/auth-prisma-context.ts @@ -1,4 +1,4 @@ -// src/server/router/context.ts +// src/server/trpc/context.ts import * as trpc from "@trpc/server"; import * as trpcNext from "@trpc/server/adapters/next"; import { unstable_getServerSession as getServerSession } from "next-auth"; @@ -7,22 +7,14 @@ import { authOptions as nextAuthOptions } from "../../pages/api/auth/[...nextaut import { prisma } from "../db/client"; export const createContext = async ( - opts?: trpcNext.CreateNextContextOptions, + opts: trpcNext.CreateNextContextOptions, ) => { - const req = opts?.req; - const res = opts?.res; - - const session = - req && res && (await getServerSession(req, res, nextAuthOptions)); + const session = await getServerSession(opts.req, opts.res, nextAuthOptions); return { - req, - res, session, prisma, }; }; -type Context = trpc.inferAsyncReturnType; - -export const createRouter = () => trpc.router(); +export type Context = trpc.inferAsyncReturnType; diff --git a/template/addons/trpc/auth-router.ts b/template/addons/trpc/auth-router.ts index 73f60a9192..73e6397c22 100644 --- a/template/addons/trpc/auth-router.ts +++ b/template/addons/trpc/auth-router.ts @@ -1,22 +1,10 @@ -import { TRPCError } from "@trpc/server"; -import { createRouter } from "./context"; +import { t, authedProcedure } from "../utils"; -export const authRouter = createRouter() - .query("getSession", { - resolve({ ctx }) { - return ctx.session; - }, - }) - .middleware(async ({ ctx, next }) => { - // Any queries or mutations after this middleware will - // raise an error unless there is a current session - if (!ctx.session) { - throw new TRPCError({ code: "UNAUTHORIZED" }); - } - return next(); - }) - .query("getSecretMessage", { - async resolve({ ctx }) { - return "You are logged in and can see this secret message!"; - }, - }); +export const authRouter = t.router({ + getSession: t.procedure.query(({ ctx }) => { + return ctx.session; + }), + getSecretMessage: authedProcedure.query(() => { + return "You are logged in and can see this secret message!"; + }), +}); diff --git a/template/addons/trpc/auth-server-utils.ts b/template/addons/trpc/auth-server-utils.ts new file mode 100644 index 0000000000..b846f71bec --- /dev/null +++ b/template/addons/trpc/auth-server-utils.ts @@ -0,0 +1,23 @@ +import { initTRPC, TRPCError } from "@trpc/server"; +import type { Context } from "./context"; +import superjson from "superjson"; + +export const t = initTRPC<{ ctx: Context }>()({ + transformer: superjson, + errorFormatter({ shape }) { + return shape; + }, +}); + +export const authedProcedure = t.procedure.use(({ ctx, next }) => { + if (!ctx.session || !ctx.session.user) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + return next({ + ctx: { + ...ctx, + // infers that `session` is non-nullable to downstream resolvers + session: { ...ctx.session, user: ctx.session.user }, + }, + }); +}); diff --git a/template/addons/trpc/base-context.ts b/template/addons/trpc/base-context.ts index c4b2f2cb62..457b91aa0b 100644 --- a/template/addons/trpc/base-context.ts +++ b/template/addons/trpc/base-context.ts @@ -2,16 +2,8 @@ import * as trpc from "@trpc/server"; import * as trpcNext from "@trpc/server/adapters/next"; -export const createContext = (opts?: trpcNext.CreateNextContextOptions) => { - const req = opts?.req; - const res = opts?.res; - - return { - req, - res, - }; +export const createContext = (opts: trpcNext.CreateNextContextOptions) => { + return {}; }; -type Context = trpc.inferAsyncReturnType; - -export const createRouter = () => trpc.router(); +export type Context = trpc.inferAsyncReturnType; diff --git a/template/addons/trpc/example-prisma-router.ts b/template/addons/trpc/example-prisma-router.ts index 367a5e4cf5..6bb0207b18 100644 --- a/template/addons/trpc/example-prisma-router.ts +++ b/template/addons/trpc/example-prisma-router.ts @@ -1,21 +1,15 @@ -import { createRouter } from "./context"; +import { t } from "../utils"; import { z } from "zod"; -export const exampleRouter = createRouter() - .query("hello", { - input: z - .object({ - text: z.string().nullish(), - }) - .nullish(), - resolve({ input }) { +export const exampleRouter = t.router({ + hello: t.procedure + .input(z.object({ text: z.string().nullish() }).nullish()) + .query(({ input }) => { return { greeting: `Hello ${input?.text ?? "world"}`, }; - }, - }) - .query("getAll", { - async resolve({ ctx }) { - return await ctx.prisma.example.findMany(); - }, - }); + }), + getAll: t.procedure.query(({ ctx }) => { + return ctx.prisma.example.findMany(); + }), +}); diff --git a/template/addons/trpc/example-router.ts b/template/addons/trpc/example-router.ts index 0da6668dfc..12215755e3 100644 --- a/template/addons/trpc/example-router.ts +++ b/template/addons/trpc/example-router.ts @@ -1,15 +1,12 @@ -import { createRouter } from "./context"; +import { t } from "../utils"; import { z } from "zod"; -export const exampleRouter = createRouter().query("hello", { - input: z - .object({ - text: z.string().nullish(), - }) - .nullish(), - resolve({ input }) { - return { - greeting: `Hello ${input?.text ?? "world"}`, - }; - }, +export const exampleRouter = t.router({ + hello: t.procedure + .input(z.object({ text: z.string().nullish() }).nullish()) + .query(({ input }) => { + return { + greeting: `Hello ${input?.text ?? "world"}`, + }; + }), }); diff --git a/template/addons/trpc/index-router.ts b/template/addons/trpc/index-router.ts index b5cddc54b2..36b6930e60 100644 --- a/template/addons/trpc/index-router.ts +++ b/template/addons/trpc/index-router.ts @@ -1,12 +1,11 @@ // src/server/router/index.ts -import { createRouter } from "./context"; -import superjson from "superjson"; +import { t } from "../utils"; import { exampleRouter } from "./example"; -export const appRouter = createRouter() - .transformer(superjson) - .merge("example.", exampleRouter); +export const appRouter = t.router({ + example: exampleRouter, +}); // export type definition of API export type AppRouter = typeof appRouter; diff --git a/template/addons/trpc/prisma-context.ts b/template/addons/trpc/prisma-context.ts index 175f33a9be..eb6964c23c 100644 --- a/template/addons/trpc/prisma-context.ts +++ b/template/addons/trpc/prisma-context.ts @@ -3,17 +3,10 @@ import * as trpc from "@trpc/server"; import * as trpcNext from "@trpc/server/adapters/next"; import { prisma } from "../db/client"; -export const createContext = (opts?: trpcNext.CreateNextContextOptions) => { - const req = opts?.req; - const res = opts?.res; - +export const createContext = (opts: trpcNext.CreateNextContextOptions) => { return { - req, - res, prisma, }; }; -type Context = trpc.inferAsyncReturnType; - -export const createRouter = () => trpc.router(); +export type Context = trpc.inferAsyncReturnType; diff --git a/template/addons/trpc/server-utils.ts b/template/addons/trpc/server-utils.ts new file mode 100644 index 0000000000..e9e499cf78 --- /dev/null +++ b/template/addons/trpc/server-utils.ts @@ -0,0 +1,10 @@ +import { initTRPC } from "@trpc/server"; +import type { Context } from "./context"; +import superjson from "superjson"; + +export const t = initTRPC<{ ctx: Context }>()({ + transformer: superjson, + errorFormatter({ shape }) { + return shape; + }, +}); diff --git a/template/addons/trpc/utils.ts b/template/addons/trpc/utils.ts index f1c6ac9b1a..3e2277c2a0 100644 --- a/template/addons/trpc/utils.ts +++ b/template/addons/trpc/utils.ts @@ -1,10 +1,42 @@ // src/utils/trpc.ts -import type { AppRouter } from "../server/router"; -import { createReactQueryHooks } from "@trpc/react"; +import { setupTRPC } from "@trpc/next"; +import type { inferProcedureInput, inferProcedureOutput } from "@trpc/server"; +import type { AppRouter } from "../server/trpc/router"; +import superjson from "superjson"; -export const trpc = createReactQueryHooks(); +const getBaseUrl = () => { + if (typeof window !== "undefined") return ""; // browser should use relative url + if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url + + return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost +}; + +export const trpc = setupTRPC({ + config() { + return { + url: `${getBaseUrl()}/api/trpc`, + transformer: superjson, + }; + }, + ssr: false, +}); /** - * Check out tRPC docs for Inference Helpers - * https://trpc.io/docs/infer-types#inference-helpers + * This is a helper method to infer the output of a query resolver + * @example type HelloOutput = inferQueryOutput<'hello'> */ +export type inferQueryOutput< + TRouteKey extends keyof AppRouter["_def"]["queries"], +> = inferProcedureOutput; + +export type inferQueryInput< + TRouteKey extends keyof AppRouter["_def"]["queries"], +> = inferProcedureInput; + +export type inferMutationOutput< + TRouteKey extends keyof AppRouter["_def"]["mutations"], +> = inferProcedureOutput; + +export type inferMutationInput< + TRouteKey extends keyof AppRouter["_def"]["mutations"], +> = inferProcedureInput; diff --git a/template/page-studs/_app/with-auth-trpc.tsx b/template/page-studs/_app/with-auth-trpc.tsx index 11007c1129..08efc7e58a 100644 --- a/template/page-studs/_app/with-auth-trpc.tsx +++ b/template/page-studs/_app/with-auth-trpc.tsx @@ -1,50 +1,15 @@ // src/pages/_app.tsx -import { withTRPC } from "@trpc/next"; -import type { AppRouter } from "../server/router"; -import type { AppType } from "next/dist/shared/lib/utils"; -import superjson from "superjson"; -import { SessionProvider } from "next-auth/react"; import "../styles/globals.css"; +import { SessionProvider } from "next-auth/react"; +import type { AppType } from "next/dist/shared/lib/utils"; +import { trpc } from "../utils/trpc"; -const MyApp: AppType = ({ - Component, - pageProps: { session, ...pageProps }, -}) => { +const MyApp: AppType = ({ Component, pageProps }) => { return ( - + ); }; -const getBaseUrl = () => { - if (typeof window !== "undefined") { - return ""; - } - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url - - return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost -}; - -export default withTRPC({ - config({ ctx }) { - /** - * If you want to use SSR, you need to use the server's full URL - * @link https://trpc.io/docs/ssr - */ - const url = `${getBaseUrl()}/api/trpc`; - - return { - url, - transformer: superjson, - /** - * @link https://react-query.tanstack.com/reference/QueryClient - */ - // queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } }, - }; - }, - /** - * @link https://trpc.io/docs/ssr - */ - ssr: false, -})(MyApp); +export default trpc.withTRPC(MyApp); diff --git a/template/page-studs/_app/with-trpc.tsx b/template/page-studs/_app/with-trpc.tsx index b40aefcbf7..1217d3638d 100644 --- a/template/page-studs/_app/with-trpc.tsx +++ b/template/page-studs/_app/with-trpc.tsx @@ -1,43 +1,10 @@ // src/pages/_app.tsx -import { withTRPC } from "@trpc/next"; -import type { AppRouter } from "../server/router"; -import type { AppType } from "next/dist/shared/lib/utils"; -import superjson from "superjson"; import "../styles/globals.css"; +import type { AppType } from "next/dist/shared/lib/utils"; +import { trpc } from "../utils/trpc"; const MyApp: AppType = ({ Component, pageProps }) => { return ; }; -const getBaseUrl = () => { - if (typeof window !== "undefined") { - return ""; - } - if (process.browser) return ""; // Browser should use current path - if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url - - return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost -}; - -export default withTRPC({ - config({ ctx }) { - /** - * If you want to use SSR, you need to use the server's full URL - * @link https://trpc.io/docs/ssr - */ - const url = `${getBaseUrl()}/api/trpc`; - - return { - url, - transformer: superjson, - /** - * @link https://react-query.tanstack.com/reference/QueryClient - */ - // queryClientConfig: { defaultOptions: { queries: { staleTime: 60 } } }, - }; - }, - /** - * @link https://trpc.io/docs/ssr - */ - ssr: false, -})(MyApp); +export default trpc.withTRPC(MyApp); diff --git a/template/page-studs/index/with-auth-trpc-tw.tsx b/template/page-studs/index/with-auth-trpc-tw.tsx index fb424eece9..f9095aa6b8 100644 --- a/template/page-studs/index/with-auth-trpc-tw.tsx +++ b/template/page-studs/index/with-auth-trpc-tw.tsx @@ -3,35 +3,8 @@ import Head from "next/head"; import { signIn, signOut, useSession } from "next-auth/react"; import { trpc } from "../utils/trpc"; -type TechnologyCardProps = { - name: string; - description: string; - documentation: string; -}; - -const AuthShowcase: React.FC = () => { - const { data: secretMessage, isLoading } = trpc.useQuery([ - "auth.getSecretMessage", - ]); - - const { data: sessionData } = useSession(); - - return ( -
- {sessionData &&

Logged in as {sessionData?.user?.name}

} - {secretMessage &&

{secretMessage}

} - -
- ); -}; - const Home: NextPage = () => { - const hello = trpc.useQuery(["example.hello", { text: "from tRPC" }]); + const hello = trpc.proxy.example.hello.useQuery({ text: "from tRPC" }); return ( <> @@ -40,8 +13,7 @@ const Home: NextPage = () => { - -
+

Create T3 App

@@ -78,7 +50,7 @@ const Home: NextPage = () => { documentation="https://www.prisma.io/docs/" /> -
+
{hello.data ?

{hello.data.greeting}

:

Loading..

}
@@ -86,6 +58,34 @@ const Home: NextPage = () => { ); }; +export default Home; + +// Component to showcase protected routes using Auth +const AuthShowcase: React.FC = () => { + const { data: secretMessage } = trpc.proxy.auth.getSecretMessage.useQuery(); + const { data: sessionData } = useSession(); + + return ( +
+ {sessionData &&

Logged in as {sessionData?.user?.name}

} + {secretMessage &&

{secretMessage}

} + +
+ ); +}; + +// Technology component +type TechnologyCardProps = { + name: string; + description: string; + documentation: string; +}; + const TechnologyCard = ({ name, description, @@ -106,5 +106,3 @@ const TechnologyCard = ({ ); }; - -export default Home; diff --git a/template/page-studs/index/with-auth-trpc.tsx b/template/page-studs/index/with-auth-trpc.tsx index c71ac4ce2b..8eb6a1dd81 100644 --- a/template/page-studs/index/with-auth-trpc.tsx +++ b/template/page-studs/index/with-auth-trpc.tsx @@ -5,12 +5,8 @@ import { trpc } from "../utils/trpc"; import { signIn, signOut, useSession } from "next-auth/react"; const AuthShowcase: React.FC = () => { - const { data: secretMessage, isLoading } = trpc.useQuery([ - "auth.getSecretMessage", - ]); - + const { data: secretMessage } = trpc.proxy.auth.getSecretMessage.useQuery(); const { data: sessionData } = useSession(); - return (
{sessionData &&

Logged in as {sessionData?.user?.name}

} @@ -22,25 +18,9 @@ const AuthShowcase: React.FC = () => { ); }; -interface TechnologyProps { - name: string; - description: string; - documentation: string; -} - -const Technology: React.FC = (props) => { - return ( - <> -
  • - - {props.name} - -
  • - - ); -}; - const Home: NextPage = () => { + const hello = trpc.proxy.example.hello.useQuery({ text: "from tRPC" }); + return ( <> @@ -73,6 +53,9 @@ const Home: NextPage = () => { documentation={"https://www.typescriptlang.org/"} /> +
    + {hello.data ?

    {hello.data.greeting}

    :

    Loading..

    } +
    @@ -81,3 +64,21 @@ const Home: NextPage = () => { }; export default Home; + +interface TechnologyProps { + name: string; + description: string; + documentation: string; +} + +const Technology: React.FC = (props) => { + return ( + <> +
  • + + {props.name} + +
  • + + ); +}; diff --git a/template/page-studs/index/with-trpc-tw.tsx b/template/page-studs/index/with-trpc-tw.tsx index 9fd858dea4..3fc086f665 100644 --- a/template/page-studs/index/with-trpc-tw.tsx +++ b/template/page-studs/index/with-trpc-tw.tsx @@ -2,14 +2,8 @@ import type { NextPage } from "next"; import Head from "next/head"; import { trpc } from "../utils/trpc"; -type TechnologyCardProps = { - name: string; - description: string; - documentation: string; -}; - const Home: NextPage = () => { - const hello = trpc.useQuery(["example.hello", { text: "from tRPC" }]); + const hello = trpc.proxy.example.hello.useQuery({ text: "from tRPC" }); return ( <> @@ -18,8 +12,7 @@ const Home: NextPage = () => { - -
    +

    Create T3 App

    @@ -46,7 +39,7 @@ const Home: NextPage = () => { documentation="https://trpc.io/" /> -
    +
    {hello.data ?

    {hello.data.greeting}

    :

    Loading..

    }
    @@ -54,6 +47,15 @@ const Home: NextPage = () => { ); }; +export default Home; + +// Technology component +interface TechnologyCardProps { + name: string; + description: string; + documentation: string; +} + const TechnologyCard = ({ name, description, @@ -74,5 +76,3 @@ const TechnologyCard = ({ ); }; - -export default Home; diff --git a/template/page-studs/index/with-trpc.tsx b/template/page-studs/index/with-trpc.tsx index 28b0b0e176..11e4ddcb31 100644 --- a/template/page-studs/index/with-trpc.tsx +++ b/template/page-studs/index/with-trpc.tsx @@ -3,10 +3,7 @@ import Head from "next/head"; import { trpc } from "../utils/trpc"; const Home: NextPage = () => { - const { data, isLoading } = trpc.useQuery([ - "example.hello", - { text: "from tRPC" }, - ]); + const hello = trpc.proxy.example.hello.useQuery({ text: "from tRPC" }); return ( <> @@ -44,7 +41,9 @@ const Home: NextPage = () => { -
    {data ?

    {data.greeting}

    :

    Loading..

    }
    +
    + {hello.data ?

    {hello.data.greeting}

    :

    Loading..

    } +
    diff --git a/template/page-studs/index/with-tw.tsx b/template/page-studs/index/with-tw.tsx index e8a49897e0..c4b1e48945 100644 --- a/template/page-studs/index/with-tw.tsx +++ b/template/page-studs/index/with-tw.tsx @@ -1,12 +1,6 @@ import type { NextPage } from "next"; import Head from "next/head"; -type TechnologyCardProps = { - name: string; - description: string; - documentation: string; -}; - const Home: NextPage = () => { return ( <> @@ -15,8 +9,7 @@ const Home: NextPage = () => { - -
    +

    Create T3 App

    @@ -43,6 +36,15 @@ const Home: NextPage = () => { ); }; +export default Home; + +// Technology component +interface TechnologyCardProps { + name: string; + description: string; + documentation: string; +} + const TechnologyCard = ({ name, description, @@ -63,5 +65,3 @@ const TechnologyCard = ({ ); }; - -export default Home; From 61e0d7a3c906df7c72b9a240e28e2c240f0ba2d9 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sat, 16 Jul 2022 22:19:53 +0200 Subject: [PATCH 02/14] chore(release): bump next version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6f2e6cee82..fbc0bd772f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-t3-app", - "version": "5.0.0", + "version": "5.0.0-next.0", "description": "Create web application with the t3 stack", "author": "Shoubhit Dash ", "license": "MIT", From e95c7afc0b17812e86298e1aa8f80ad3e34d78ba Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Tue, 19 Jul 2022 21:37:12 +0200 Subject: [PATCH 03/14] chore: update @next from 5.0.0 to 5.2.1 (#227) * chore: bring main changes into next * fix: lock rq at v3 for now --- .github/workflows/PR-CI.yml | 4 +- .lintstagedrc | 9 +++++ CHANGELOG.md | 31 ++++++++++++++++ README.md | 2 + package.json | 11 +----- src/cli/index.ts | 2 +- src/helpers/createProject.ts | 23 +++++++++++- src/helpers/initGit.ts | 7 ---- src/helpers/installPackages.ts | 10 ++--- src/helpers/scaffoldProject.ts | 4 ++ src/installers/envVars.ts | 18 ++++++--- src/installers/index.ts | 12 ++++-- src/installers/next-auth.ts | 7 +--- src/installers/prisma.ts | 8 +--- src/installers/tailwind.ts | 7 +--- src/installers/trpc.ts | 9 +---- src/utils/runPkgManagerInstall.ts | 37 +++++++++++++++++-- .../addons/env/{env-auth.js => env-auth.mjs} | 6 +-- ...env-prisma-auth.js => env-prisma-auth.mjs} | 6 +-- .../env/{env-prisma.js => env-prisma.mjs} | 6 +-- .../addons/next-auth/api-handler-prisma.ts | 2 +- template/addons/next-auth/api-handler.ts | 2 +- template/addons/prisma/auth-schema.prisma | 3 +- template/addons/prisma/client.ts | 2 +- template/addons/prisma/schema.prisma | 3 +- template/base/.env-example | 2 +- template/base/_gitignore | 1 + template/base/next.config.js | 8 ---- template/base/next.config.mjs | 18 +++++++++ template/base/src/server/env-schema.js | 7 ---- template/base/src/server/env-schema.mjs | 6 +++ template/base/src/server/{env.js => env.mjs} | 14 +++---- template/base/tsconfig.json | 3 +- 33 files changed, 177 insertions(+), 113 deletions(-) create mode 100644 .lintstagedrc rename template/addons/env/{env-auth.js => env-auth.mjs} (58%) rename template/addons/env/{env-prisma-auth.js => env-prisma-auth.mjs} (70%) rename template/addons/env/{env-prisma.js => env-prisma.mjs} (50%) delete mode 100644 template/base/next.config.js create mode 100644 template/base/next.config.mjs delete mode 100644 template/base/src/server/env-schema.js create mode 100644 template/base/src/server/env-schema.mjs rename template/base/src/server/{env.js => env.mjs} (54%) diff --git a/.github/workflows/PR-CI.yml b/.github/workflows/PR-CI.yml index 24dd524032..bd0f252a59 100644 --- a/.github/workflows/PR-CI.yml +++ b/.github/workflows/PR-CI.yml @@ -5,7 +5,7 @@ name: PR-CI on: pull_request: branches: ["*"] - + jobs: install-deps: runs-on: ubuntu-latest @@ -31,7 +31,6 @@ jobs: if: steps.pnpm-cache.outputs.cache-hit != 'true' - run: pnpm install - lint: runs-on: ubuntu-latest name: Run ESLint @@ -125,5 +124,4 @@ jobs: # has to be scaffolded outside the CLI project so that no lint/tsconfig are leaking # through. this way it ensures that it is the app's configs that are being used - run: pnpm start -y ../ci-test-app - - run: mv ../ci-test-app/.env-example ../ci-test-app/.env - run: cd ../ci-test-app && pnpm build diff --git a/.lintstagedrc b/.lintstagedrc new file mode 100644 index 0000000000..3c83c1195e --- /dev/null +++ b/.lintstagedrc @@ -0,0 +1,9 @@ +{ + "src/**/*.{ts,tsx}": [ + "prettier --write", + "eslint --fix" + ], + "*.{json,md,mdx}": [ + "prettier --write" + ] +} diff --git a/CHANGELOG.md b/CHANGELOG.md index ac4724d9f9..adc9a9d88f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,37 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [5.2.0](https://github.com/t3-oss/create-t3-app/compare/v5.1.1...v5.2.0) (2022-07-19) + +### Features + +- add db.sqlite-journal to gitignore ([#221](https://github.com/t3-oss/create-t3-app/issues/221)) ([0604d21](https://github.com/t3-oss/create-t3-app/commit/0604d217f0dfe3d165013395c1a801e6bc32a17c)) + +### Bug Fixes + +- handle pkg with version in noInstall mode ([#220](https://github.com/t3-oss/create-t3-app/issues/220)) ([5737beb](https://github.com/t3-oss/create-t3-app/commit/5737bebaab3ff6f955bf825274e25419a91642d6)) +- missing DATABASE_URL ([#222](https://github.com/t3-oss/create-t3-app/issues/222)) ([b6164d8](https://github.com/t3-oss/create-t3-app/commit/b6164d8944330564e9911e47f36d82620986c8c5)) +- typo - rename 'varibles' to 'variables' ([#223](https://github.com/t3-oss/create-t3-app/issues/223)) ([fc6519f](https://github.com/t3-oss/create-t3-app/commit/fc6519f66d067e4e348c7f4c00f6287dc5c40c3d)) +- typo in readme ([9390400](https://github.com/t3-oss/create-t3-app/commit/9390400b8720240c650e5dbde7ca0718b7835757)) + +### [5.1.1](https://github.com/t3-oss/create-t3-app/compare/v5.1.0...v5.1.1) (2022-07-18) + +### Bug Fixes + +- add missing trpc import ([37b6283](https://github.com/t3-oss/create-t3-app/commit/37b6283bb56e44092fc9ef2d65c84e5298e51d22)) + +## [5.1.0](https://github.com/t3-oss/create-t3-app/compare/v5.0.0...v5.1.0) (2022-07-18) + +### Features + +- add trpc inference helpers ([1fcec81](https://github.com/t3-oss/create-t3-app/commit/1fcec81a74372ce5cfaf6503d63f16d5e2b12e3c)) +- migrate env + next config to esm ([#205](https://github.com/t3-oss/create-t3-app/issues/205)) ([59f8ad4](https://github.com/t3-oss/create-t3-app/commit/59f8ad4f2104aa1f11508b55da75c333dce03625)) + +### Bug Fixes + +- **#218:** lock react-query to v3 ([15ea5f1](https://github.com/t3-oss/create-t3-app/commit/15ea5f1dc8b86430a720f2eaeb6ccdfaab629892)), closes [#218](https://github.com/t3-oss/create-t3-app/issues/218) +- move gitignore rename call ([#215](https://github.com/t3-oss/create-t3-app/issues/215)) ([7111033](https://github.com/t3-oss/create-t3-app/commit/7111033d75c858041f06d69e95c51e40515457fc)) + ## 5.0.0 (2022-07-16) ### Features diff --git a/README.md b/README.md index 3fa64ea20b..33eae82878 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,8 @@ yarn create t3-app pnpm dlx create-t3-app@latest ``` +If you prefer using the [experimental v10 version of tRPC](https://github.com/trpc/trpc/blob/next/.tmp/v10-docs.md), use `create-t3-app@next`. Note that the alpha versions of tRPC that it uses may contain API changes. We will try our best to keep on top of these, please file an issue if we have missed something. + An ongoing development branch, `create-t3-app@beta`, can be downloaded for the most recent changes. Expect bugs when using the `beta` branch and please open issues with reproductions when they occur.

    CLI Docs

    diff --git a/package.json b/package.json index fbc0bd772f..f4c07c9a08 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-t3-app", - "version": "5.0.0-next.0", + "version": "5.2.1-next.1", "description": "Create web application with the t3 stack", "author": "Shoubhit Dash ", "license": "MIT", @@ -69,14 +69,5 @@ "tsup": "^6.1.2", "type-fest": "^2.14.0", "typescript": "^4.7.4" - }, - "lint-staged": { - "src/**/*.{ts,tsx}": [ - "prettier --write", - "eslint --fix" - ], - "*.{json,md,mdx}": [ - "prettier --write" - ] } } diff --git a/src/cli/index.ts b/src/cli/index.ts index 88ade0c612..b88d382edc 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -141,7 +141,7 @@ export const runCli = async () => { type: "checkbox", message: "Which packages would you like to enable?", choices: availablePackages - .filter((pkg) => pkg !== "envVaribles") // dont prompt for env-vars + .filter((pkg) => pkg !== "envVariables") // dont prompt for env-vars .map((pkgName) => ({ name: pkgName, checked: false, diff --git a/src/helpers/createProject.ts b/src/helpers/createProject.ts index 41ca04dc39..f5d28c0d73 100644 --- a/src/helpers/createProject.ts +++ b/src/helpers/createProject.ts @@ -1,6 +1,7 @@ import type { PkgInstallerMap } from "../installers/index.js"; import path from "path"; import { getUserPkgManager } from "../utils/getUserPkgManager.js"; +import { curryRunPkgManagerInstall } from "../utils/runPkgManagerInstall.js"; import { installPackages } from "./installPackages.js"; import { scaffoldProject } from "./scaffoldProject.js"; import { selectAppFile, selectIndexFile } from "./selectBoilerplate.js"; @@ -18,12 +19,30 @@ export const createProject = async ({ }: CreateProjectOptions) => { const pkgManager = getUserPkgManager(); const projectDir = path.resolve(process.cwd(), projectName); + const runPkgManagerInstall = curryRunPkgManagerInstall({ + projectDir, + pkgManager, + devMode: false, + noInstallMode: noInstall, + }); // Bootstraps the base Next.js application - await scaffoldProject({ projectName, projectDir, pkgManager, noInstall }); + await scaffoldProject({ + projectName, + projectDir, + pkgManager, + noInstall, + runPkgManagerInstall, + }); // Install the selected packages - await installPackages({ projectDir, pkgManager, packages, noInstall }); + await installPackages({ + projectDir, + pkgManager, + packages, + noInstall, + runPkgManagerInstall, + }); // TODO: Look into using handlebars or other templating engine to scaffold without needing to maintain multiple copies of the same file await selectAppFile({ projectDir, packages }); diff --git a/src/helpers/initGit.ts b/src/helpers/initGit.ts index 4ce64f86a7..1938625c9b 100644 --- a/src/helpers/initGit.ts +++ b/src/helpers/initGit.ts @@ -1,6 +1,4 @@ -import path from "path"; import chalk from "chalk"; -import fs from "fs-extra"; import ora from "ora"; import { execa } from "../utils/execAsync.js"; import { logger } from "../utils/logger.js"; @@ -32,9 +30,4 @@ export const initializeGit = async (projectDir: string) => { )} could not initialize git. Update git to the latest version!\n`, ); } - - await fs.rename( - path.join(projectDir, "_gitignore"), - path.join(projectDir, ".gitignore"), - ); }; diff --git a/src/helpers/installPackages.ts b/src/helpers/installPackages.ts index c572549b4b..849ca13e98 100644 --- a/src/helpers/installPackages.ts +++ b/src/helpers/installPackages.ts @@ -7,12 +7,8 @@ type InstallPackagesOptions = { packages: PkgInstallerMap; } & InstallerOptions; // This runs the installer for all the packages that the user has selected -export const installPackages = async ({ - projectDir, - pkgManager, - packages, - noInstall, -}: InstallPackagesOptions) => { +export const installPackages = async (options: InstallPackagesOptions) => { + const { packages, noInstall } = options; logger.info(`${noInstall ? "Adding" : "Installing"} packages...`); for (const [name, pkgOpts] of Object.entries(packages)) { @@ -20,7 +16,7 @@ export const installPackages = async ({ const spinner = ora( `${noInstall ? "Adding" : "Installing"} ${name}...`, ).start(); - await pkgOpts.installer({ projectDir, pkgManager, packages, noInstall }); + await pkgOpts.installer(options); spinner.succeed( chalk.green( `Successfully ${noInstall ? "added" : "installed"} ${chalk.green.bold( diff --git a/src/helpers/scaffoldProject.ts b/src/helpers/scaffoldProject.ts index 07b6ac6bcb..e965085889 100644 --- a/src/helpers/scaffoldProject.ts +++ b/src/helpers/scaffoldProject.ts @@ -57,6 +57,10 @@ export const scaffoldProject = async ({ spinner.start(); await fs.copy(srcDir, projectDir); + await fs.rename( + path.join(projectDir, "_gitignore"), + path.join(projectDir, ".gitignore"), + ); if (!noInstall) { await execa(`${pkgManager} install`, { cwd: projectDir }); diff --git a/src/installers/envVars.ts b/src/installers/envVars.ts index b3f25a7888..409c63be0f 100644 --- a/src/installers/envVars.ts +++ b/src/installers/envVars.ts @@ -3,7 +3,7 @@ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "../consts.js"; -export const envVariblesInstaller: Installer = async ({ +export const envVariablesInstaller: Installer = async ({ projectDir, packages, }) => { @@ -16,20 +16,26 @@ export const envVariblesInstaller: Installer = async ({ switch (true) { case usingAuth && usingPrisma: - envFile = "env-prisma-auth.js"; + envFile = "env-prisma-auth.mjs"; break; case usingAuth: - envFile = "env-auth.js"; + envFile = "env-auth.mjs"; break; case usingPrisma: - envFile = "env-prisma.js"; + envFile = "env-prisma.mjs"; break; } if (!envFile) return; const envSchemaSrc = path.join(envAssetDir, envFile); - const envSchemaDest = path.join(projectDir, "src/server/env-schema.js"); + const envSchemaDest = path.join(projectDir, "src/server/env-schema.mjs"); - await fs.copy(envSchemaSrc, envSchemaDest, { overwrite: true }); + const envExample = path.join(projectDir, ".env-example"); + const envDest = path.join(projectDir, ".env"); + + await Promise.all([ + fs.copy(envSchemaSrc, envSchemaDest, { overwrite: true }), + fs.rename(envExample, envDest), + ]); }; diff --git a/src/installers/index.ts b/src/installers/index.ts index 3e0b04a17d..175cb83887 100644 --- a/src/installers/index.ts +++ b/src/installers/index.ts @@ -1,5 +1,6 @@ import type { PackageManager } from "../utils/getUserPkgManager.js"; -import { envVariblesInstaller } from "./envVars.js"; +import type { CurriedRunPkgManagerInstallOptions } from "../utils/runPkgManagerInstall.js"; +import { envVariablesInstaller } from "./envVars.js"; import { nextAuthInstaller } from "./next-auth.js"; import { prismaInstaller } from "./prisma.js"; import { tailwindInstaller } from "./tailwind.js"; @@ -12,7 +13,7 @@ export const availablePackages = [ "prisma", "tailwind", "trpc", - "envVaribles", + "envVariables", ] as const; export type AvailablePackages = typeof availablePackages[number]; @@ -23,6 +24,9 @@ export interface InstallerOptions { noInstall: boolean; packages?: PkgInstallerMap; projectName?: string; + runPkgManagerInstall: ( + opts: CurriedRunPkgManagerInstallOptions, + ) => Promise; } export type Installer = (opts: InstallerOptions) => Promise; @@ -53,8 +57,8 @@ export const buildPkgInstallerMap = ( inUse: packages.includes("trpc"), installer: trpcInstaller, }, - envVaribles: { + envVariables: { inUse: packages.includes("prisma") || packages.includes("nextAuth"), - installer: envVariblesInstaller, + installer: envVariablesInstaller, }, }); diff --git a/src/installers/next-auth.ts b/src/installers/next-auth.ts index f5227fdcfa..1c47d1ad95 100644 --- a/src/installers/next-auth.ts +++ b/src/installers/next-auth.ts @@ -2,23 +2,18 @@ import type { Installer } from "./index.js"; import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "../consts.js"; -import { runPkgManagerInstall } from "../utils/runPkgManagerInstall.js"; export const nextAuthInstaller: Installer = async ({ - pkgManager, projectDir, + runPkgManagerInstall, packages, - noInstall, }) => { await runPkgManagerInstall({ - pkgManager, - projectDir, packages: [ "next-auth", packages?.prisma.inUse ? "@next-auth/prisma-adapter" : "", ], devMode: false, - noInstallMode: noInstall, }); const nextAuthAssetDir = path.join(PKG_ROOT, "template/addons/next-auth"); diff --git a/src/installers/prisma.ts b/src/installers/prisma.ts index 179046ea9c..1e5da0cc2c 100644 --- a/src/installers/prisma.ts +++ b/src/installers/prisma.ts @@ -4,27 +4,21 @@ import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "../consts.js"; import { execa } from "../utils/execAsync.js"; -import { runPkgManagerInstall } from "../utils/runPkgManagerInstall.js"; export const prismaInstaller: Installer = async ({ projectDir, + runPkgManagerInstall, pkgManager, packages, noInstall, }) => { await runPkgManagerInstall({ - pkgManager, - projectDir, packages: ["prisma"], devMode: true, - noInstallMode: noInstall, }); await runPkgManagerInstall({ - pkgManager, - projectDir, packages: ["@prisma/client"], devMode: false, - noInstallMode: noInstall, }); const prismaAssetDir = path.join(PKG_ROOT, "template/addons/prisma"); diff --git a/src/installers/tailwind.ts b/src/installers/tailwind.ts index fc7275bba9..e77158d894 100644 --- a/src/installers/tailwind.ts +++ b/src/installers/tailwind.ts @@ -2,19 +2,14 @@ import type { Installer } from "./index.js"; import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "../consts.js"; -import { runPkgManagerInstall } from "../utils/runPkgManagerInstall.js"; export const tailwindInstaller: Installer = async ({ projectDir, - pkgManager, - noInstall, + runPkgManagerInstall, }) => { await runPkgManagerInstall({ - pkgManager, - projectDir, packages: ["tailwindcss", "postcss", "autoprefixer"], devMode: true, - noInstallMode: noInstall, }); const twAssetDir = path.join(PKG_ROOT, "template/addons/tailwind"); diff --git a/src/installers/trpc.ts b/src/installers/trpc.ts index cd76b6da80..603c4b913d 100644 --- a/src/installers/trpc.ts +++ b/src/installers/trpc.ts @@ -2,19 +2,15 @@ import type { Installer } from "./index.js"; import path from "path"; import fs from "fs-extra"; import { PKG_ROOT } from "../consts.js"; -import { runPkgManagerInstall } from "../utils/runPkgManagerInstall.js"; export const trpcInstaller: Installer = async ({ projectDir, - pkgManager, packages, - noInstall, + runPkgManagerInstall, }) => { await runPkgManagerInstall({ - pkgManager, - projectDir, packages: [ - "react-query", + "react-query@3", "superjson", "@trpc/server@experimental", "@trpc/client@experimental", @@ -22,7 +18,6 @@ export const trpcInstaller: Installer = async ({ "@trpc/react@experimental", ], devMode: false, - noInstallMode: noInstall, }); const usingAuth = packages?.nextAuth.inUse; const usingPrisma = packages?.prisma.inUse; diff --git a/src/utils/runPkgManagerInstall.ts b/src/utils/runPkgManagerInstall.ts index 6052a676b4..8e6bce3b0b 100644 --- a/src/utils/runPkgManagerInstall.ts +++ b/src/utils/runPkgManagerInstall.ts @@ -5,13 +5,17 @@ import { type PackageJson } from "type-fest"; import { execa } from "./execAsync.js"; import { logger } from "./logger.js"; -export const runPkgManagerInstall = async (opts: { +export interface RunPkgManagerInstallOptions { pkgManager: PackageManager; devMode: boolean; projectDir: string; packages: string[]; noInstallMode: boolean; -}) => { +} + +export const runPkgManagerInstall = async ( + opts: RunPkgManagerInstallOptions, +) => { const { pkgManager, devMode, projectDir, packages, noInstallMode } = opts; if (noInstallMode) { @@ -30,11 +34,13 @@ export const runPkgManagerInstall = async (opts: { continue; } + const pkgName = pkg.replace(/^(@?[^@]+)(?:@.+)?$/, "$1"); + // Note: We know that pkgJson.[dev]Dependencies exists in the base Next.js template so we don't need to validate it if (devMode) { - pkgJson.devDependencies![pkg] = `^${latestVersion.trim()}`; //eslint-disable-line @typescript-eslint/no-non-null-assertion + pkgJson.devDependencies![pkgName] = `^${latestVersion.trim()}`; //eslint-disable-line @typescript-eslint/no-non-null-assertion } else { - pkgJson.dependencies![pkg] = `^${latestVersion.trim()}`; //eslint-disable-line @typescript-eslint/no-non-null-assertion + pkgJson.dependencies![pkgName] = `^${latestVersion.trim()}`; //eslint-disable-line @typescript-eslint/no-non-null-assertion } } @@ -50,3 +56,26 @@ export const runPkgManagerInstall = async (opts: { const fullCmd = `${installCmd} ${flag} ${packages.join(" ")}`; await execa(fullCmd, { cwd: projectDir }); }; + +export type CurryRunPkgManagerInstallOptions = Omit< + RunPkgManagerInstallOptions, + "packages" +>; + +export type CurriedRunPkgManagerInstallOptions = + Partial & + Omit; + +export const curryRunPkgManagerInstall = ( + baseOptions: CurryRunPkgManagerInstallOptions, +) => { + const curriedRunPkgManagerInstall = async ( + options: CurriedRunPkgManagerInstallOptions, + ) => + runPkgManagerInstall({ + ...baseOptions, + ...options, + }); + + return curriedRunPkgManagerInstall; +}; diff --git a/template/addons/env/env-auth.js b/template/addons/env/env-auth.mjs similarity index 58% rename from template/addons/env/env-auth.js rename to template/addons/env/env-auth.mjs index d060f1e17b..2a8a4b5d52 100644 --- a/template/addons/env/env-auth.js +++ b/template/addons/env/env-auth.mjs @@ -1,10 +1,8 @@ -const { z } = require("zod"); +import { z } from "zod"; -const envSchema = z.object({ +export const envSchema = z.object({ NEXTAUTH_SECRET: z.string(), NEXTAUTH_URL: z.string().url(), DISCORD_CLIENT_ID: z.string(), DISCORD_CLIENT_SECRET: z.string(), }); - -module.exports.envSchema = envSchema; diff --git a/template/addons/env/env-prisma-auth.js b/template/addons/env/env-prisma-auth.mjs similarity index 70% rename from template/addons/env/env-prisma-auth.js rename to template/addons/env/env-prisma-auth.mjs index 4a56e85c0b..1103bc2d32 100644 --- a/template/addons/env/env-prisma-auth.js +++ b/template/addons/env/env-prisma-auth.mjs @@ -1,6 +1,6 @@ -const { z } = require("zod"); +import { z } from "zod"; -const envSchema = z.object({ +export const envSchema = z.object({ DATABASE_URL: z.string().url(), NODE_ENV: z.enum(["development", "test", "production"]), NEXTAUTH_SECRET: z.string(), @@ -8,5 +8,3 @@ const envSchema = z.object({ DISCORD_CLIENT_ID: z.string(), DISCORD_CLIENT_SECRET: z.string(), }); - -module.exports.envSchema = envSchema; diff --git a/template/addons/env/env-prisma.js b/template/addons/env/env-prisma.mjs similarity index 50% rename from template/addons/env/env-prisma.js rename to template/addons/env/env-prisma.mjs index ca312127d7..fe171bffb7 100644 --- a/template/addons/env/env-prisma.js +++ b/template/addons/env/env-prisma.mjs @@ -1,8 +1,6 @@ -const { z } = require("zod"); +import { z } from "zod"; -const envSchema = z.object({ +export const envSchema = z.object({ DATABASE_URL: z.string().url(), NODE_ENV: z.enum(["development", "test", "production"]), }); - -module.exports.envSchema = envSchema; diff --git a/template/addons/next-auth/api-handler-prisma.ts b/template/addons/next-auth/api-handler-prisma.ts index 33dbeb113f..a93dd90136 100644 --- a/template/addons/next-auth/api-handler-prisma.ts +++ b/template/addons/next-auth/api-handler-prisma.ts @@ -5,7 +5,7 @@ import CredentialsProvider from "next-auth/providers/credentials"; // Prisma adapter for NextAuth, optional and can be removed import { PrismaAdapter } from "@next-auth/prisma-adapter"; import { prisma } from "../../../server/db/client"; -import { env } from "../../../server/env"; +import { env } from "../../../server/env.mjs"; export const authOptions: NextAuthOptions = { // Include user.id on session diff --git a/template/addons/next-auth/api-handler.ts b/template/addons/next-auth/api-handler.ts index 6125cfd4e4..a08b03a7eb 100644 --- a/template/addons/next-auth/api-handler.ts +++ b/template/addons/next-auth/api-handler.ts @@ -1,7 +1,7 @@ import NextAuth, { type NextAuthOptions } from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; import CredentialsProvider from "next-auth/providers/credentials"; -import { env } from "../../../server/env"; +import { env } from "../../../server/env.mjs"; export const authOptions: NextAuthOptions = { // Include user.id on session diff --git a/template/addons/prisma/auth-schema.prisma b/template/addons/prisma/auth-schema.prisma index 934f962bb0..c443186e58 100644 --- a/template/addons/prisma/auth-schema.prisma +++ b/template/addons/prisma/auth-schema.prisma @@ -7,8 +7,7 @@ generator client { datasource db { provider = "sqlite" - url = "file:./db.sqlite" - // url = env("DATABASE_URL") + url = env("DATABASE_URL") } model Example { diff --git a/template/addons/prisma/client.ts b/template/addons/prisma/client.ts index fc97065fcf..90d484597c 100644 --- a/template/addons/prisma/client.ts +++ b/template/addons/prisma/client.ts @@ -1,6 +1,6 @@ // src/server/db/client.ts import { PrismaClient } from "@prisma/client"; -import { env } from "../env"; +import { env } from "../env.mjs"; declare global { var prisma: PrismaClient | undefined; diff --git a/template/addons/prisma/schema.prisma b/template/addons/prisma/schema.prisma index 90cd79af9a..34ba4eb7d5 100644 --- a/template/addons/prisma/schema.prisma +++ b/template/addons/prisma/schema.prisma @@ -7,8 +7,7 @@ generator client { datasource db { provider = "sqlite" - url = "file:./db.sqlite" - // url = env("DATABASE_URL") + url = env("DATABASE_URL") } model Example { diff --git a/template/base/.env-example b/template/base/.env-example index 2a25fe514f..bc003984c4 100644 --- a/template/base/.env-example +++ b/template/base/.env-example @@ -1,7 +1,7 @@ # Note that not all variables here might be in use for your selected configuration # Prisma -DATABASE_URL=postgresql://postgres:@localhost:5832/db +DATABASE_URL=file:./db.sqlite # Next Auth NEXTAUTH_SECRET= diff --git a/template/base/_gitignore b/template/base/_gitignore index 4948f300a8..d9bbc5dac0 100644 --- a/template/base/_gitignore +++ b/template/base/_gitignore @@ -10,6 +10,7 @@ # database /prisma/db.sqlite +/prisma/db.sqlite-journal # next.js /.next/ diff --git a/template/base/next.config.js b/template/base/next.config.js deleted file mode 100644 index b08bad63b1..0000000000 --- a/template/base/next.config.js +++ /dev/null @@ -1,8 +0,0 @@ -const { env } = require("./src/server/env"); - -/** @type {import('next').NextConfig} */ -const nextConfig = { - reactStrictMode: true, -}; - -module.exports = nextConfig; diff --git a/template/base/next.config.mjs b/template/base/next.config.mjs new file mode 100644 index 0000000000..ffc23ee467 --- /dev/null +++ b/template/base/next.config.mjs @@ -0,0 +1,18 @@ +// @ts-check +import { env } from "./src/server/env.mjs"; + +/** + * Don't be scared of the generics here. + * All they do is to give us autocompletion when using this. + * + * @template {import('next').NextConfig} T + * @param {T} config - A generic parameter that flows through to the return type + * @constraint {{import('next').NextConfig}} + */ +function defineNextConfig(config) { + return config; +} + +export default defineNextConfig({ + reactStrictMode: true, +}); diff --git a/template/base/src/server/env-schema.js b/template/base/src/server/env-schema.js deleted file mode 100644 index 4998588c02..0000000000 --- a/template/base/src/server/env-schema.js +++ /dev/null @@ -1,7 +0,0 @@ -const { z } = require("zod"); - -const envSchema = z.object({ - // Specify your environment variables schema here -}); - -module.exports.envSchema = envSchema; diff --git a/template/base/src/server/env-schema.mjs b/template/base/src/server/env-schema.mjs new file mode 100644 index 0000000000..41a1a61f56 --- /dev/null +++ b/template/base/src/server/env-schema.mjs @@ -0,0 +1,6 @@ +// @ts-check +import { z } from "zod"; + +export const envSchema = z.object({ + // Specify your environment variables schema here +}); diff --git a/template/base/src/server/env.js b/template/base/src/server/env.mjs similarity index 54% rename from template/base/src/server/env.js rename to template/base/src/server/env.mjs index 5baca953d0..0a2ca7c37a 100644 --- a/template/base/src/server/env.js +++ b/template/base/src/server/env.mjs @@ -1,11 +1,11 @@ // @ts-check /** - * This file is included in `/next.config.js` which ensures the app isn't built with invalid env vars. - * It has to be a `.js`-file to be imported there. + * This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars. + * It has to be a `.mjs`-file to be imported there. */ -const { envSchema } = require("./env-schema"); +import { envSchema } from "./env-schema.mjs"; -const env = envSchema.safeParse(process.env); +const _env = envSchema.safeParse(process.env); const formatErrors = ( /** @type {import('zod').ZodFormattedError,string>} */ @@ -18,12 +18,12 @@ const formatErrors = ( }) .filter(Boolean); -if (!env.success) { +if (!_env.success) { console.error( "❌ Invalid environment variables:\n", - ...formatErrors(env.error.format()), + ...formatErrors(_env.error.format()), ); process.exit(1); } -module.exports.env = env.data; +export const env = _env.data; diff --git a/template/base/tsconfig.json b/template/base/tsconfig.json index 70373f2862..b1913ac90b 100644 --- a/template/base/tsconfig.json +++ b/template/base/tsconfig.json @@ -21,7 +21,8 @@ "next-auth.d.ts", "**/*.ts", "**/*.tsx", - "**/*.js" + "**/*.js", + "**/*.mjs" ], "exclude": ["node_modules"] } From 546bb2ad048040e45c6ba9cac230b1425f3bad26 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Tue, 19 Jul 2022 21:45:00 +0200 Subject: [PATCH 04/14] fix: lock rq v3 properly --- src/installers/trpc.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/installers/trpc.ts b/src/installers/trpc.ts index 603c4b913d..a6bb75d1a8 100644 --- a/src/installers/trpc.ts +++ b/src/installers/trpc.ts @@ -10,7 +10,7 @@ export const trpcInstaller: Installer = async ({ }) => { await runPkgManagerInstall({ packages: [ - "react-query@3", + "react-query@3.39.2", "superjson", "@trpc/server@experimental", "@trpc/client@experimental", From 8860a74e4bd8a29252da60ba319e1481775e598b Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Tue, 19 Jul 2022 21:45:32 +0200 Subject: [PATCH 05/14] chore(relase): bump @next version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f4c07c9a08..a767853068 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-t3-app", - "version": "5.2.1-next.1", + "version": "5.2.1-next.2", "description": "Create web application with the t3 stack", "author": "Shoubhit Dash ", "license": "MIT", From ac0c5a6a99092d7f557ccea2b6027224fc5a8bad Mon Sep 17 00:00:00 2001 From: Shoubhit Dash Date: Thu, 21 Jul 2022 22:22:17 +0530 Subject: [PATCH 06/14] chore: bump next version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a767853068..3384e43bfb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-t3-app", - "version": "5.2.1-next.2", + "version": "5.2.1-next.3", "description": "Create web application with the t3 stack", "author": "Shoubhit Dash ", "license": "MIT", From 3f2a253c8b8d0c56356e74b67c6b872303b66437 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Thu, 21 Jul 2022 23:47:59 +0200 Subject: [PATCH 07/14] chore(release): add pub:next script --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 3384e43bfb..b344471023 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "prepare": "husky install", "release": "standard-version", "pub:beta": "npm run build && npm publish --tag beta", + "pub:next": "npm run build && npm publish --tag next", "pub:release": "npm run build && npm publish" }, "dependencies": { From d0209f23048940e31f4315f75900a88bc23d3286 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 22 Jul 2022 12:16:50 +0200 Subject: [PATCH 08/14] chore: update to Next.js 12.2.3 --- template/base/next.config.mjs | 1 + template/base/package.json | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/template/base/next.config.mjs b/template/base/next.config.mjs index ffc23ee467..55bcddbafc 100644 --- a/template/base/next.config.mjs +++ b/template/base/next.config.mjs @@ -15,4 +15,5 @@ function defineNextConfig(config) { export default defineNextConfig({ reactStrictMode: true, + swcMinify: true, }); diff --git a/template/base/package.json b/template/base/package.json index bd32791faf..6ef17772df 100644 --- a/template/base/package.json +++ b/template/base/package.json @@ -9,7 +9,7 @@ "lint": "next lint" }, "dependencies": { - "next": "12.2.1", + "next": "12.2.3", "react": "18.2.0", "react-dom": "18.2.0", "zod": "^3.17.3" @@ -19,7 +19,7 @@ "@types/react": "18.0.14", "@types/react-dom": "18.0.5", "eslint": "8.18.0", - "eslint-config-next": "12.2.1", + "eslint-config-next": "12.2.3", "typescript": "4.7.4" } } From 82d08bb9f743101a1fc85ef6cf30bb54b22edf91 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Fri, 22 Jul 2022 12:17:03 +0200 Subject: [PATCH 09/14] chore(release): bump next version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b344471023..295f74c369 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-t3-app", - "version": "5.2.1-next.3", + "version": "5.2.1-next.4", "description": "Create web application with the t3 stack", "author": "Shoubhit Dash ", "license": "MIT", From 075e3f234055ff5e6a41e3e48d9ac7b3d901fc8d Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 24 Jul 2022 15:42:44 +0200 Subject: [PATCH 10/14] chore: update next-branch --- CHANGELOG.md | 6 ++++ README.md | 21 +++++++++++- package.json | 6 ++-- src/cli/index.ts | 14 ++++---- src/consts.ts | 6 +++- src/helpers/createProject.ts | 12 +++---- src/helpers/initGit.ts | 4 +-- src/helpers/installPackages.ts | 4 +-- src/helpers/logNextSteps.ts | 8 ++--- src/helpers/scaffoldProject.ts | 8 ++--- src/helpers/selectBoilerplate.ts | 4 +-- src/index.ts | 16 ++++----- src/installers/envVars.ts | 6 ++-- src/installers/index.ts | 14 ++++---- src/installers/next-auth.ts | 4 +-- src/installers/prisma.ts | 6 ++-- src/installers/tailwind.ts | 4 +-- src/installers/trpc.ts | 4 +-- src/utils/getT3Version.ts | 2 +- src/utils/renderTitle.ts | 13 +++++--- src/utils/runPkgManagerInstall.ts | 6 ++-- .../addons/next-auth/api-handler-prisma.ts | 17 +--------- template/addons/next-auth/api-handler.ts | 17 +--------- template/addons/prisma/client.ts | 2 +- template/base/src/env/client.mjs | 33 +++++++++++++++++++ template/base/src/env/schema.mjs | 22 +++++++++++++ template/base/src/env/server.mjs | 27 +++++++++++++++ template/base/src/server/env-schema.mjs | 6 ---- template/base/src/server/env.mjs | 29 ---------------- tsconfig.json | 6 ++++ 30 files changed, 191 insertions(+), 136 deletions(-) create mode 100644 template/base/src/env/client.mjs create mode 100644 template/base/src/env/schema.mjs create mode 100644 template/base/src/env/server.mjs delete mode 100644 template/base/src/server/env-schema.mjs delete mode 100644 template/base/src/server/env.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index adc9a9d88f..9865908668 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [5.2.1](https://github.com/t3-oss/create-t3-app/compare/v5.2.0...v5.2.1) (2022-07-19) + +### Bug Fixes + +- update url in auth prisma schema ([97c9e27](https://github.com/t3-oss/create-t3-app/commit/97c9e279bf7465a47f29add10c8a6d4c9b7339a5)) + ## [5.2.0](https://github.com/t3-oss/create-t3-app/compare/v5.1.1...v5.2.0) (2022-07-19) ### Features diff --git a/README.md b/README.md index 33eae82878..6918f2e241 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@
    -[![PRs-Welcome][contribute-image]][contribute-url] [![NPM version][npm-image]][npm-url] +[![PRs-Welcome][contribute-image]][contribute-url] [![Discord](https://img.shields.io/discord/966627436387266600?color=%235865F2&label=Discord&logo=discord&logoColor=%23fff)](https://t3.gg/discord) [![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url]
    @@ -35,6 +35,7 @@ - What is the T3 Stack? - Getting Started +- Community - CLI Docs - T3 Axioms - Contributors @@ -88,6 +89,12 @@ If you prefer using the [experimental v10 version of tRPC](https://github.com/tr An ongoing development branch, `create-t3-app@beta`, can be downloaded for the most recent changes. Expect bugs when using the `beta` branch and please open issues with reproductions when they occur. +

    Community

    + +For help, discussion about best practices, or any other conversation that would benefit create-t3-app: + +[Join the T3 Discord Server](https://t3.gg/discord) +

    CLI Docs

    The following CLI options and flags can configure the create command with custom behavior: @@ -99,6 +106,18 @@ The following CLI options and flags can configure the create command with custom | `-y`, `--default` | Bypass the CLI and use all default options to bootstrap a new t3-app | | `--noInstall` | Generate project without installing dependencies | +To use the beta version of `create-t3-app`: + +```bash +npx create-t3-app@beta +``` + +To scaffold the project using tRPC v10 (experimental): + +```bash +npx create-t3-app@next +``` +

    T3 Axioms

    We'll be frank - this is an _opinionated project_. We share a handful of core beliefs around building and we treat them as the basis for our decisions. diff --git a/package.json b/package.json index 295f74c369..d3ef2da93a 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "start": "node dist/index.js", "lint": "eslint src/ --fix", "lint:check": "eslint src/ --cache --cache-strategy content", - "format": "prettier --write \"**/*.{ts,tsx,md,mdx,json}\"", - "format:check": "prettier --check \"**/*.{ts,tsx,md,mdx,json}\"", + "format": "prettier --write \"**/*.{ts,tsx,md,mdx,json,js,mjs,cjs}\"", + "format:check": "prettier --check \"**/*.{ts,tsx,md,mdx,json,js,mjs,cjs}\"", "prepare": "husky install", "release": "standard-version", "pub:beta": "npm run build && npm publish --tag beta", @@ -43,7 +43,6 @@ "dependencies": { "chalk": "5.0.1", "commander": "^9.3.0", - "figlet": "^1.5.2", "fs-extra": "^10.1.0", "gradient-string": "^2.0.1", "inquirer": "^9.0.0", @@ -52,7 +51,6 @@ "devDependencies": { "@commitlint/cli": "^17.0.3", "@commitlint/config-conventional": "^17.0.3", - "@types/figlet": "^1.5.4", "@types/fs-extra": "^9.0.13", "@types/gradient-string": "^1.1.2", "@types/inquirer": "^8.2.1", diff --git a/src/cli/index.ts b/src/cli/index.ts index b88d382edc..d129f4d48e 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -1,13 +1,13 @@ -import type { AvailablePackages } from "../installers/index.js"; +import type { AvailablePackages } from "~/installers/index.js"; import chalk from "chalk"; import { Command } from "commander"; import inquirer from "inquirer"; -import { CREATE_T3_APP, DEFAULT_APP_NAME } from "../consts.js"; -import { availablePackages } from "../installers/index.js"; -import { getVersion } from "../utils/getT3Version.js"; -import { getUserPkgManager } from "../utils/getUserPkgManager.js"; -import { logger } from "../utils/logger.js"; -import { validateAppName } from "../utils/validateAppName.js"; +import { CREATE_T3_APP, DEFAULT_APP_NAME } from "~/consts.js"; +import { availablePackages } from "~/installers/index.js"; +import { getVersion } from "~/utils/getT3Version.js"; +import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; +import { logger } from "~/utils/logger.js"; +import { validateAppName } from "~/utils/validateAppName.js"; interface CliFlags { noGit: boolean; diff --git a/src/consts.ts b/src/consts.ts index f2afcaed70..df474167d2 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -9,6 +9,10 @@ export const PKG_ROOT = path.join(distPath, "../"); //export const PKG_ROOT = path.dirname(require.main.filename); -export const TITLE_TEXT = `CREATE T3 APP`; +export const TITLE_TEXT = ` ___ ___ ___ __ _____ ___ _____ ____ __ ___ ___ + / __| _ \\ __| / \\_ _| __| |_ _|__ / / \\ | _ \\ _ \\ + | (__| / _| / /\\ \\| | | _| | | |_ \\ / /\\ \\| _/ _/ + \\___|_|_\\___|_/‾‾\\_\\_| |___| |_| |___/ /_/‾‾\\_\\_| |_| +`; export const DEFAULT_APP_NAME = "my-t3-app"; export const CREATE_T3_APP = "create-t3-app"; diff --git a/src/helpers/createProject.ts b/src/helpers/createProject.ts index f5d28c0d73..07fbd05875 100644 --- a/src/helpers/createProject.ts +++ b/src/helpers/createProject.ts @@ -1,10 +1,10 @@ -import type { PkgInstallerMap } from "../installers/index.js"; +import type { PkgInstallerMap } from "~/installers/index.js"; import path from "path"; -import { getUserPkgManager } from "../utils/getUserPkgManager.js"; -import { curryRunPkgManagerInstall } from "../utils/runPkgManagerInstall.js"; -import { installPackages } from "./installPackages.js"; -import { scaffoldProject } from "./scaffoldProject.js"; -import { selectAppFile, selectIndexFile } from "./selectBoilerplate.js"; +import { installPackages } from "~/helpers/installPackages.js"; +import { scaffoldProject } from "~/helpers/scaffoldProject.js"; +import { selectAppFile, selectIndexFile } from "~/helpers/selectBoilerplate.js"; +import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; +import { curryRunPkgManagerInstall } from "~/utils/runPkgManagerInstall.js"; interface CreateProjectOptions { projectName: string; diff --git a/src/helpers/initGit.ts b/src/helpers/initGit.ts index 1938625c9b..76cedc06ac 100644 --- a/src/helpers/initGit.ts +++ b/src/helpers/initGit.ts @@ -1,7 +1,7 @@ import chalk from "chalk"; import ora from "ora"; -import { execa } from "../utils/execAsync.js"; -import { logger } from "../utils/logger.js"; +import { execa } from "~/utils/execAsync.js"; +import { logger } from "~/utils/logger.js"; // This initializes the Git-repository for the project export const initializeGit = async (projectDir: string) => { diff --git a/src/helpers/installPackages.ts b/src/helpers/installPackages.ts index 849ca13e98..41c8a76e0e 100644 --- a/src/helpers/installPackages.ts +++ b/src/helpers/installPackages.ts @@ -1,7 +1,7 @@ -import type { InstallerOptions, PkgInstallerMap } from "../installers/index.js"; +import type { InstallerOptions, PkgInstallerMap } from "~/installers/index.js"; import chalk from "chalk"; import ora from "ora"; -import { logger } from "../utils/logger.js"; +import { logger } from "~/utils/logger.js"; type InstallPackagesOptions = { packages: PkgInstallerMap; diff --git a/src/helpers/logNextSteps.ts b/src/helpers/logNextSteps.ts index 0f6ad1fa95..026c6761ba 100644 --- a/src/helpers/logNextSteps.ts +++ b/src/helpers/logNextSteps.ts @@ -1,7 +1,7 @@ -import type { InstallerOptions } from "../installers/index.js"; -import { DEFAULT_APP_NAME } from "../consts.js"; -import { getUserPkgManager } from "../utils/getUserPkgManager.js"; -import { logger } from "../utils/logger.js"; +import type { InstallerOptions } from "~/installers/index.js"; +import { DEFAULT_APP_NAME } from "~/consts.js"; +import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; +import { logger } from "~/utils/logger.js"; // This logs the next steps that the user should take in order to advance the project export const logNextSteps = ({ diff --git a/src/helpers/scaffoldProject.ts b/src/helpers/scaffoldProject.ts index e965085889..527e087c66 100644 --- a/src/helpers/scaffoldProject.ts +++ b/src/helpers/scaffoldProject.ts @@ -3,10 +3,10 @@ import chalk from "chalk"; import fs from "fs-extra"; import inquirer from "inquirer"; import ora from "ora"; -import { PKG_ROOT } from "../consts.js"; -import { InstallerOptions } from "../installers/index.js"; -import { execa } from "../utils/execAsync.js"; -import { logger } from "../utils/logger.js"; +import { PKG_ROOT } from "~/consts.js"; +import { InstallerOptions } from "~/installers/index.js"; +import { execa } from "~/utils/execAsync.js"; +import { logger } from "~/utils/logger.js"; // This bootstraps the base Next.js application export const scaffoldProject = async ({ diff --git a/src/helpers/selectBoilerplate.ts b/src/helpers/selectBoilerplate.ts index d5d4bad942..7350250ae6 100644 --- a/src/helpers/selectBoilerplate.ts +++ b/src/helpers/selectBoilerplate.ts @@ -1,7 +1,7 @@ -import type { InstallerOptions } from "../installers/index.js"; +import type { InstallerOptions } from "~/installers/index.js"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; +import { PKG_ROOT } from "~/consts.js"; type SelectBoilerplateProps = Required< Pick diff --git a/src/index.ts b/src/index.ts index bd7992b110..e1a7a5321f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,14 +2,14 @@ import type { PackageJson } from "type-fest"; import path from "path"; import fs from "fs-extra"; -import { runCli } from "./cli/index.js"; -import { createProject } from "./helpers/createProject.js"; -import { initializeGit } from "./helpers/initGit.js"; -import { logNextSteps } from "./helpers/logNextSteps.js"; -import { buildPkgInstallerMap } from "./installers/index.js"; -import { logger } from "./utils/logger.js"; -import { parseNameAndPath } from "./utils/parseNameAndPath.js"; -import { renderTitle } from "./utils/renderTitle.js"; +import { runCli } from "~/cli/index.js"; +import { createProject } from "~/helpers/createProject.js"; +import { initializeGit } from "~/helpers/initGit.js"; +import { logNextSteps } from "~/helpers/logNextSteps.js"; +import { buildPkgInstallerMap } from "~/installers/index.js"; +import { logger } from "~/utils/logger.js"; +import { parseNameAndPath } from "~/utils/parseNameAndPath.js"; +import { renderTitle } from "~/utils/renderTitle.js"; const main = async () => { renderTitle(); diff --git a/src/installers/envVars.ts b/src/installers/envVars.ts index 409c63be0f..a00ccc0f0f 100644 --- a/src/installers/envVars.ts +++ b/src/installers/envVars.ts @@ -1,7 +1,7 @@ -import type { Installer } from "./index.js"; +import type { Installer } from "~/installers/index.js"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; +import { PKG_ROOT } from "~/consts.js"; export const envVariablesInstaller: Installer = async ({ projectDir, @@ -29,7 +29,7 @@ export const envVariablesInstaller: Installer = async ({ if (!envFile) return; const envSchemaSrc = path.join(envAssetDir, envFile); - const envSchemaDest = path.join(projectDir, "src/server/env-schema.mjs"); + const envSchemaDest = path.join(projectDir, "src/env/env-schema.mjs"); const envExample = path.join(projectDir, ".env-example"); const envDest = path.join(projectDir, ".env"); diff --git a/src/installers/index.ts b/src/installers/index.ts index 175cb83887..d23c2301b3 100644 --- a/src/installers/index.ts +++ b/src/installers/index.ts @@ -1,10 +1,10 @@ -import type { PackageManager } from "../utils/getUserPkgManager.js"; -import type { CurriedRunPkgManagerInstallOptions } from "../utils/runPkgManagerInstall.js"; -import { envVariablesInstaller } from "./envVars.js"; -import { nextAuthInstaller } from "./next-auth.js"; -import { prismaInstaller } from "./prisma.js"; -import { tailwindInstaller } from "./tailwind.js"; -import { trpcInstaller } from "./trpc.js"; +import type { PackageManager } from "~/utils/getUserPkgManager.js"; +import type { CurriedRunPkgManagerInstallOptions } from "~/utils/runPkgManagerInstall.js"; +import { envVariablesInstaller } from "~/installers/envVars.js"; +import { nextAuthInstaller } from "~/installers/next-auth.js"; +import { prismaInstaller } from "~/installers/prisma.js"; +import { tailwindInstaller } from "~/installers/tailwind.js"; +import { trpcInstaller } from "~/installers/trpc.js"; // Turning this into a const allows the list to be iterated over for programatically creating prompt options // Should increase extensability in the future diff --git a/src/installers/next-auth.ts b/src/installers/next-auth.ts index 1c47d1ad95..2ed6f372f8 100644 --- a/src/installers/next-auth.ts +++ b/src/installers/next-auth.ts @@ -1,7 +1,7 @@ -import type { Installer } from "./index.js"; +import type { Installer } from "~/installers/index.js"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; +import { PKG_ROOT } from "~/consts.js"; export const nextAuthInstaller: Installer = async ({ projectDir, diff --git a/src/installers/prisma.ts b/src/installers/prisma.ts index 1e5da0cc2c..71bafe193c 100644 --- a/src/installers/prisma.ts +++ b/src/installers/prisma.ts @@ -1,9 +1,9 @@ -import type { Installer } from "./index.js"; import type { PackageJson } from "type-fest"; +import type { Installer } from "~/installers/index.js"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; -import { execa } from "../utils/execAsync.js"; +import { PKG_ROOT } from "~/consts.js"; +import { execa } from "~/utils/execAsync.js"; export const prismaInstaller: Installer = async ({ projectDir, diff --git a/src/installers/tailwind.ts b/src/installers/tailwind.ts index e77158d894..089ae31ee2 100644 --- a/src/installers/tailwind.ts +++ b/src/installers/tailwind.ts @@ -1,7 +1,7 @@ -import type { Installer } from "./index.js"; +import type { Installer } from "~/installers/index.js"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; +import { PKG_ROOT } from "~/consts.js"; export const tailwindInstaller: Installer = async ({ projectDir, diff --git a/src/installers/trpc.ts b/src/installers/trpc.ts index a6bb75d1a8..eb0ca6b993 100644 --- a/src/installers/trpc.ts +++ b/src/installers/trpc.ts @@ -1,7 +1,7 @@ -import type { Installer } from "./index.js"; +import type { Installer } from "~/installers/index.js"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; +import { PKG_ROOT } from "~/consts.js"; export const trpcInstaller: Installer = async ({ projectDir, diff --git a/src/utils/getT3Version.ts b/src/utils/getT3Version.ts index af4bd3eb98..881df1f9f3 100644 --- a/src/utils/getT3Version.ts +++ b/src/utils/getT3Version.ts @@ -1,7 +1,7 @@ import type { PackageJson } from "type-fest"; import path from "path"; import fs from "fs-extra"; -import { PKG_ROOT } from "../consts.js"; +import { PKG_ROOT } from "~/consts.js"; export const getVersion = () => { const packageJsonPath = path.join(PKG_ROOT, "package.json"); diff --git a/src/utils/renderTitle.ts b/src/utils/renderTitle.ts index bde46c9875..f0c8c42c71 100644 --- a/src/utils/renderTitle.ts +++ b/src/utils/renderTitle.ts @@ -1,6 +1,6 @@ -import figlet from "figlet"; import gradient from "gradient-string"; -import { TITLE_TEXT } from "../consts.js"; +import { TITLE_TEXT } from "~/consts.js"; +import { getUserPkgManager } from "~/utils/getUserPkgManager.js"; // colors brought in from vscode poimandres theme const poimandresTheme = { @@ -13,7 +13,12 @@ const poimandresTheme = { }; export const renderTitle = () => { - const text = figlet.textSync(TITLE_TEXT, { font: "Small" }); const t3Gradient = gradient(Object.values(poimandresTheme)); - console.log("\n", t3Gradient.multiline(text)); + + // resolves weird behavior where the ascii is offset + const pkgManager = getUserPkgManager(); + if (pkgManager === "yarn" || pkgManager === "pnpm") { + console.log(""); + } + console.log(t3Gradient.multiline(TITLE_TEXT)); }; diff --git a/src/utils/runPkgManagerInstall.ts b/src/utils/runPkgManagerInstall.ts index 8e6bce3b0b..24d47bd725 100644 --- a/src/utils/runPkgManagerInstall.ts +++ b/src/utils/runPkgManagerInstall.ts @@ -1,9 +1,9 @@ -import type { PackageManager } from "./getUserPkgManager.js"; +import type { PackageManager } from "~/utils/getUserPkgManager.js"; import path from "path"; import fs from "fs-extra"; import { type PackageJson } from "type-fest"; -import { execa } from "./execAsync.js"; -import { logger } from "./logger.js"; +import { execa } from "~/utils/execAsync.js"; +import { logger } from "~/utils/logger.js"; export interface RunPkgManagerInstallOptions { pkgManager: PackageManager; diff --git a/template/addons/next-auth/api-handler-prisma.ts b/template/addons/next-auth/api-handler-prisma.ts index a93dd90136..bcead858bb 100644 --- a/template/addons/next-auth/api-handler-prisma.ts +++ b/template/addons/next-auth/api-handler-prisma.ts @@ -1,11 +1,10 @@ import NextAuth, { type NextAuthOptions } from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; -import CredentialsProvider from "next-auth/providers/credentials"; // Prisma adapter for NextAuth, optional and can be removed import { PrismaAdapter } from "@next-auth/prisma-adapter"; import { prisma } from "../../../server/db/client"; -import { env } from "../../../server/env.mjs"; +import { env } from "../../../env/server.mjs"; export const authOptions: NextAuthOptions = { // Include user.id on session @@ -25,20 +24,6 @@ export const authOptions: NextAuthOptions = { clientSecret: env.DISCORD_CLIENT_SECRET, }), // ...add more providers here - CredentialsProvider({ - name: "Credentials", - credentials: { - name: { - label: "Name", - type: "text", - placeholder: "Enter your name", - }, - }, - async authorize(credentials, _req) { - const user = { id: 1, name: credentials?.name ?? "J Smith" }; - return user; - }, - }), ], }; diff --git a/template/addons/next-auth/api-handler.ts b/template/addons/next-auth/api-handler.ts index a08b03a7eb..2d1d2dab47 100644 --- a/template/addons/next-auth/api-handler.ts +++ b/template/addons/next-auth/api-handler.ts @@ -1,7 +1,6 @@ import NextAuth, { type NextAuthOptions } from "next-auth"; import DiscordProvider from "next-auth/providers/discord"; -import CredentialsProvider from "next-auth/providers/credentials"; -import { env } from "../../../server/env.mjs"; +import { env } from "../../../env/server.mjs"; export const authOptions: NextAuthOptions = { // Include user.id on session @@ -20,20 +19,6 @@ export const authOptions: NextAuthOptions = { clientSecret: env.DISCORD_CLIENT_SECRET, }), // ...add more providers here - CredentialsProvider({ - name: "Credentials", - credentials: { - name: { - label: "Name", - type: "text", - placeholder: "Enter your name", - }, - }, - async authorize(credentials, _req) { - const user = { id: 1, name: credentials?.name ?? "J Smith" }; - return user; - }, - }), ], }; diff --git a/template/addons/prisma/client.ts b/template/addons/prisma/client.ts index 90d484597c..cf0f1b7cd0 100644 --- a/template/addons/prisma/client.ts +++ b/template/addons/prisma/client.ts @@ -1,6 +1,6 @@ // src/server/db/client.ts import { PrismaClient } from "@prisma/client"; -import { env } from "../env.mjs"; +import { env } from "../../server.mjs"; declare global { var prisma: PrismaClient | undefined; diff --git a/template/base/src/env/client.mjs b/template/base/src/env/client.mjs new file mode 100644 index 0000000000..63e6845cc6 --- /dev/null +++ b/template/base/src/env/client.mjs @@ -0,0 +1,33 @@ +// @ts-check +import { clientEnv, clientEnvSchema } from "./schema.mjs"; + +const _clientEnv = clientEnvSchema.safeParse(clientEnv); + +export const formatErrors = ( + /** @type {import('zod').ZodFormattedError,string>} */ + errors, +) => + Object.entries(errors) + .map(([name, value]) => { + if (value && "_errors" in value) + return `${name}: ${value._errors.join(", ")}\n`; + }) + .filter(Boolean); + +if (!_clientEnv.success) { + console.error( + "❌ Invalid environment variables:\n", + ...formatErrors(_clientEnv.error.format()), + ); + throw new Error("Invalid environment variables"); +} + +for (let key of Object.keys(_clientEnv.data)) { + if (!key.startsWith("NEXT_PUBLIC_")) { + console.warn("❌ Invalid public environment variable name:", key); + + throw new Error("Invalid public environment variable name"); + } +} + +export const env = _clientEnv.data; diff --git a/template/base/src/env/schema.mjs b/template/base/src/env/schema.mjs new file mode 100644 index 0000000000..c2089eb417 --- /dev/null +++ b/template/base/src/env/schema.mjs @@ -0,0 +1,22 @@ +// @ts-check +import { z } from "zod"; + +export const serverEnvSchema = z.object({ + // Specify your environment variables schema here +}); + +export const clientEnvSchema = z.object({ + // Specify your client-side environment variables schema here + // Be sure to name your environment variables with the prefix "NEXT_PUBLIC_" +}); + +/** + * Next.js client-side environment variables are evaluated at build time, + * so only environment variables actually used will be included. + * + * Define your client-side environment variables in this object to + * be able to use autocompletion and destructuring in your code. + */ +export const clientEnv = { + // NEXT_PUBLIC_FOO: process.env.NEXT_PUBLIC_FOO, +}; diff --git a/template/base/src/env/server.mjs b/template/base/src/env/server.mjs new file mode 100644 index 0000000000..b83d5923c5 --- /dev/null +++ b/template/base/src/env/server.mjs @@ -0,0 +1,27 @@ +// @ts-check +/** + * This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars. + * It has to be a `.mjs`-file to be imported there. + */ +import { serverEnvSchema } from "./schema.mjs"; +import { env as clientEnv, formatErrors } from "./client.mjs"; + +const _serverEnv = serverEnvSchema.safeParse(process.env); + +if (!_serverEnv.success) { + console.error( + "❌ Invalid environment variables:\n", + ...formatErrors(_serverEnv.error.format()), + ); + throw new Error("Invalid environment variables"); +} + +for (let key of Object.keys(_serverEnv.data)) { + if (key.startsWith("NEXT_PUBLIC_")) { + console.warn("❌ You are exposing a server-side env-variable:", key); + + throw new Error("You are exposing a server-side env-variable"); + } +} + +export const env = { ..._serverEnv.data, ...clientEnv }; diff --git a/template/base/src/server/env-schema.mjs b/template/base/src/server/env-schema.mjs deleted file mode 100644 index 41a1a61f56..0000000000 --- a/template/base/src/server/env-schema.mjs +++ /dev/null @@ -1,6 +0,0 @@ -// @ts-check -import { z } from "zod"; - -export const envSchema = z.object({ - // Specify your environment variables schema here -}); diff --git a/template/base/src/server/env.mjs b/template/base/src/server/env.mjs deleted file mode 100644 index 0a2ca7c37a..0000000000 --- a/template/base/src/server/env.mjs +++ /dev/null @@ -1,29 +0,0 @@ -// @ts-check -/** - * This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars. - * It has to be a `.mjs`-file to be imported there. - */ -import { envSchema } from "./env-schema.mjs"; - -const _env = envSchema.safeParse(process.env); - -const formatErrors = ( - /** @type {import('zod').ZodFormattedError,string>} */ - errors, -) => - Object.entries(errors) - .map(([name, value]) => { - if (value && "_errors" in value) - return `${name}: ${value._errors.join(", ")}\n`; - }) - .filter(Boolean); - -if (!_env.success) { - console.error( - "❌ Invalid environment variables:\n", - ...formatErrors(_env.error.format()), - ); - process.exit(1); -} - -export const env = _env.data; diff --git a/tsconfig.json b/tsconfig.json index 215c7892b8..11e2b5f82a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -35,6 +35,12 @@ // "exactOptionalPropertyTypes": true, // TLDR - Setting to undefined is not the same as a property not being defined at all // "noPropertyAccessFromIndexSignature": true, // TLDR - Use dot notation for objects if youre sure it exists, use ['index'] notaion if unsure + /* MODULE PATH ALIAS*/ + "baseUrl": "./", + "paths": { + "~/*": ["./src/*"] + }, + /* OTHER OPTIONS */ "allowSyntheticDefaultImports": true, "esModuleInterop": true, From 2b60773306911b2a6d3091230154e159d5f62eb0 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 24 Jul 2022 17:51:56 +0200 Subject: [PATCH 11/14] chore: update env stuff from main --- template/addons/env/auth-prisma-schema.mjs | 33 ++++++++++++++++++++++ template/addons/env/auth-schema.mjs | 31 ++++++++++++++++++++ template/addons/env/env-auth.mjs | 8 ------ template/addons/env/env-prisma-auth.mjs | 10 ------- template/addons/env/env-prisma.mjs | 6 ---- template/addons/env/prisma-schema.mjs | 29 +++++++++++++++++++ template/addons/prisma/client.ts | 2 +- template/base/next.config.mjs | 2 +- template/base/src/env/client.mjs | 4 +-- template/base/src/env/schema.mjs | 28 ++++++++++-------- template/base/src/env/server.mjs | 4 +-- 11 files changed, 116 insertions(+), 41 deletions(-) create mode 100644 template/addons/env/auth-prisma-schema.mjs create mode 100644 template/addons/env/auth-schema.mjs delete mode 100644 template/addons/env/env-auth.mjs delete mode 100644 template/addons/env/env-prisma-auth.mjs delete mode 100644 template/addons/env/env-prisma.mjs create mode 100644 template/addons/env/prisma-schema.mjs diff --git a/template/addons/env/auth-prisma-schema.mjs b/template/addons/env/auth-prisma-schema.mjs new file mode 100644 index 0000000000..5871158c2c --- /dev/null +++ b/template/addons/env/auth-prisma-schema.mjs @@ -0,0 +1,33 @@ +// @ts-check +import { z } from "zod"; + +/** + * Specify your server-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + */ +export const serverSchema = z.object({ + DATABASE_URL: z.string().url(), + NODE_ENV: z.enum(["development", "test", "production"]), + NEXTAUTH_SECRET: z.string(), + NEXTAUTH_URL: z.string().url(), + DISCORD_CLIENT_ID: z.string(), + DISCORD_CLIENT_SECRET: z.string(), +}); + +/** + * Specify your client-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + * To expose them to the client, prefix them with `NEXT_PUBLIC_`. + */ +export const clientSchema = z.object({ + // NEXT_PUBLIC_BAR: z.string(), +}); + +/** + * You can't destruct `process.env` as a regular object, so you have to do + * it manually here. This is because Next.js evaluates this at build time, + * and only used environment variables are included in the build. + */ +export const clientEnv = { + // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, +}; diff --git a/template/addons/env/auth-schema.mjs b/template/addons/env/auth-schema.mjs new file mode 100644 index 0000000000..173f7a7b0d --- /dev/null +++ b/template/addons/env/auth-schema.mjs @@ -0,0 +1,31 @@ +// @ts-check +import { z } from "zod"; + +/** + * Specify your server-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + */ +export const serverSchema = z.object({ + NEXTAUTH_SECRET: z.string(), + NEXTAUTH_URL: z.string().url(), + DISCORD_CLIENT_ID: z.string(), + DISCORD_CLIENT_SECRET: z.string(), +}); + +/** + * Specify your client-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + * To expose them to the client, prefix them with `NEXT_PUBLIC_`. + */ +export const clientSchema = z.object({ + // NEXT_PUBLIC_BAR: z.string(), +}); + +/** + * You can't destruct `process.env` as a regular object, so you have to do + * it manually here. This is because Next.js evaluates this at build time, + * and only used environment variables are included in the build. + */ +export const clientEnv = { + // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, +}; diff --git a/template/addons/env/env-auth.mjs b/template/addons/env/env-auth.mjs deleted file mode 100644 index 2a8a4b5d52..0000000000 --- a/template/addons/env/env-auth.mjs +++ /dev/null @@ -1,8 +0,0 @@ -import { z } from "zod"; - -export const envSchema = z.object({ - NEXTAUTH_SECRET: z.string(), - NEXTAUTH_URL: z.string().url(), - DISCORD_CLIENT_ID: z.string(), - DISCORD_CLIENT_SECRET: z.string(), -}); diff --git a/template/addons/env/env-prisma-auth.mjs b/template/addons/env/env-prisma-auth.mjs deleted file mode 100644 index 1103bc2d32..0000000000 --- a/template/addons/env/env-prisma-auth.mjs +++ /dev/null @@ -1,10 +0,0 @@ -import { z } from "zod"; - -export const envSchema = z.object({ - DATABASE_URL: z.string().url(), - NODE_ENV: z.enum(["development", "test", "production"]), - NEXTAUTH_SECRET: z.string(), - NEXTAUTH_URL: z.string().url(), - DISCORD_CLIENT_ID: z.string(), - DISCORD_CLIENT_SECRET: z.string(), -}); diff --git a/template/addons/env/env-prisma.mjs b/template/addons/env/env-prisma.mjs deleted file mode 100644 index fe171bffb7..0000000000 --- a/template/addons/env/env-prisma.mjs +++ /dev/null @@ -1,6 +0,0 @@ -import { z } from "zod"; - -export const envSchema = z.object({ - DATABASE_URL: z.string().url(), - NODE_ENV: z.enum(["development", "test", "production"]), -}); diff --git a/template/addons/env/prisma-schema.mjs b/template/addons/env/prisma-schema.mjs new file mode 100644 index 0000000000..4927fa4c4d --- /dev/null +++ b/template/addons/env/prisma-schema.mjs @@ -0,0 +1,29 @@ +// @ts-check +import { z } from "zod"; + +/** + * Specify your server-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + */ +export const serverSchema = z.object({ + DATABASE_URL: z.string().url(), + NODE_ENV: z.enum(["development", "test", "production"]), +}); + +/** + * Specify your client-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + * To expose them to the client, prefix them with `NEXT_PUBLIC_`. + */ +export const clientSchema = z.object({ + // NEXT_PUBLIC_BAR: z.string(), +}); + +/** + * You can't destruct `process.env` as a regular object, so you have to do + * it manually here. This is because Next.js evaluates this at build time, + * and only used environment variables are included in the build. + */ +export const clientEnv = { + // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, +}; diff --git a/template/addons/prisma/client.ts b/template/addons/prisma/client.ts index cf0f1b7cd0..da43a335e2 100644 --- a/template/addons/prisma/client.ts +++ b/template/addons/prisma/client.ts @@ -1,6 +1,6 @@ // src/server/db/client.ts import { PrismaClient } from "@prisma/client"; -import { env } from "../../server.mjs"; +import { env } from "../../env/server.mjs"; declare global { var prisma: PrismaClient | undefined; diff --git a/template/base/next.config.mjs b/template/base/next.config.mjs index 55bcddbafc..5a614c5d27 100644 --- a/template/base/next.config.mjs +++ b/template/base/next.config.mjs @@ -1,5 +1,5 @@ // @ts-check -import { env } from "./src/server/env.mjs"; +import { env } from "./src/env/server.mjs"; /** * Don't be scared of the generics here. diff --git a/template/base/src/env/client.mjs b/template/base/src/env/client.mjs index 63e6845cc6..c2b360101a 100644 --- a/template/base/src/env/client.mjs +++ b/template/base/src/env/client.mjs @@ -1,7 +1,7 @@ // @ts-check -import { clientEnv, clientEnvSchema } from "./schema.mjs"; +import { clientEnv, clientSchema } from "./schema.mjs"; -const _clientEnv = clientEnvSchema.safeParse(clientEnv); +const _clientEnv = clientSchema.safeParse(clientEnv); export const formatErrors = ( /** @type {import('zod').ZodFormattedError,string>} */ diff --git a/template/base/src/env/schema.mjs b/template/base/src/env/schema.mjs index c2089eb417..cd2d6c1a30 100644 --- a/template/base/src/env/schema.mjs +++ b/template/base/src/env/schema.mjs @@ -1,22 +1,28 @@ // @ts-check import { z } from "zod"; -export const serverEnvSchema = z.object({ - // Specify your environment variables schema here +/** + * Specify your server-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + */ +export const serverSchema = z.object({ + // FOO: z.string(), }); -export const clientEnvSchema = z.object({ - // Specify your client-side environment variables schema here - // Be sure to name your environment variables with the prefix "NEXT_PUBLIC_" +/** + * Specify your client-side environment variables schema here. + * This way you can ensure the app isn't built with invalid env vars. + * To expose them to the client, prefix them with `NEXT_PUBLIC_`. + */ +export const clientSchema = z.object({ + // NEXT_PUBLIC_BAR: z.string(), }); /** - * Next.js client-side environment variables are evaluated at build time, - * so only environment variables actually used will be included. - * - * Define your client-side environment variables in this object to - * be able to use autocompletion and destructuring in your code. + * You can't destruct `process.env` as a regular object, so you have to do + * it manually here. This is because Next.js evaluates this at build time, + * and only used environment variables are included in the build. */ export const clientEnv = { - // NEXT_PUBLIC_FOO: process.env.NEXT_PUBLIC_FOO, + // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, }; diff --git a/template/base/src/env/server.mjs b/template/base/src/env/server.mjs index b83d5923c5..736e3a6bf0 100644 --- a/template/base/src/env/server.mjs +++ b/template/base/src/env/server.mjs @@ -3,10 +3,10 @@ * This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars. * It has to be a `.mjs`-file to be imported there. */ -import { serverEnvSchema } from "./schema.mjs"; +import { serverSchema } from "./schema.mjs"; import { env as clientEnv, formatErrors } from "./client.mjs"; -const _serverEnv = serverEnvSchema.safeParse(process.env); +const _serverEnv = serverSchema.safeParse(process.env); if (!_serverEnv.success) { console.error( From 5ee61b916d90fb0607881c5bcaf0b819809600b9 Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 24 Jul 2022 17:55:27 +0200 Subject: [PATCH 12/14] chore: update tailwind stuff to cjs --- src/installers/tailwind.ts | 8 ++++---- .../tailwind/{postcss.config.js => postcss.config.cjs} | 2 +- .../tailwind/{tailwind.config.js => tailwind.config.cjs} | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) rename template/addons/tailwind/{postcss.config.js => postcss.config.cjs} (96%) rename template/addons/tailwind/{tailwind.config.js => tailwind.config.cjs} (99%) diff --git a/src/installers/tailwind.ts b/src/installers/tailwind.ts index 089ae31ee2..73d5263077 100644 --- a/src/installers/tailwind.ts +++ b/src/installers/tailwind.ts @@ -14,11 +14,11 @@ export const tailwindInstaller: Installer = async ({ const twAssetDir = path.join(PKG_ROOT, "template/addons/tailwind"); - const twCfgSrc = path.join(twAssetDir, "tailwind.config.js"); - const twCfgDest = path.join(projectDir, "tailwind.config.js"); + const twCfgSrc = path.join(twAssetDir, "tailwind.config.cjs"); + const twCfgDest = path.join(projectDir, "tailwind.config.cjs"); - const postcssCfgSrc = path.join(twAssetDir, "postcss.config.js"); - const postcssCfgDest = path.join(projectDir, "postcss.config.js"); + const postcssCfgSrc = path.join(twAssetDir, "postcss.config.cjs"); + const postcssCfgDest = path.join(projectDir, "postcss.config.cjs"); const cssSrc = path.join(twAssetDir, "globals.css"); const cssDest = path.join(projectDir, "src/styles/globals.css"); diff --git a/template/addons/tailwind/postcss.config.js b/template/addons/tailwind/postcss.config.cjs similarity index 96% rename from template/addons/tailwind/postcss.config.js rename to template/addons/tailwind/postcss.config.cjs index 33ad091d26..12a703d900 100644 --- a/template/addons/tailwind/postcss.config.js +++ b/template/addons/tailwind/postcss.config.cjs @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/template/addons/tailwind/tailwind.config.js b/template/addons/tailwind/tailwind.config.cjs similarity index 99% rename from template/addons/tailwind/tailwind.config.js rename to template/addons/tailwind/tailwind.config.cjs index 9b38dc5ee1..54331dc999 100644 --- a/template/addons/tailwind/tailwind.config.js +++ b/template/addons/tailwind/tailwind.config.cjs @@ -1,5 +1,4 @@ /** @type {import('tailwindcss').Config} */ - module.exports = { content: ["./src/**/*.{js,ts,jsx,tsx}"], theme: { From 6fef68dd22aaef761ee23a77770f8bfffb2f1a1f Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Sun, 24 Jul 2022 17:57:34 +0200 Subject: [PATCH 13/14] fix: forgotten rename in env-installer --- src/installers/envVars.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/installers/envVars.ts b/src/installers/envVars.ts index a00ccc0f0f..d07709446e 100644 --- a/src/installers/envVars.ts +++ b/src/installers/envVars.ts @@ -16,20 +16,20 @@ export const envVariablesInstaller: Installer = async ({ switch (true) { case usingAuth && usingPrisma: - envFile = "env-prisma-auth.mjs"; + envFile = "auth-prisma-schema.mjs"; break; case usingAuth: - envFile = "env-auth.mjs"; + envFile = "auth-schema.mjs"; break; case usingPrisma: - envFile = "env-prisma.mjs"; + envFile = "prisma-schema.mjs"; break; } if (!envFile) return; const envSchemaSrc = path.join(envAssetDir, envFile); - const envSchemaDest = path.join(projectDir, "src/env/env-schema.mjs"); + const envSchemaDest = path.join(projectDir, "src/env/schema.mjs"); const envExample = path.join(projectDir, ".env-example"); const envDest = path.join(projectDir, ".env"); From 0537718b362937ddefe862a0552377ca89589cba Mon Sep 17 00:00:00 2001 From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com> Date: Mon, 25 Jul 2022 10:48:15 +0200 Subject: [PATCH 14/14] chore(release): update next to 5.3.0 --- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- template/addons/env/auth-prisma-schema.mjs | 1 + template/addons/env/auth-schema.mjs | 1 + template/addons/env/prisma-schema.mjs | 1 + template/base/src/env/schema.mjs | 1 + 6 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9865908668..a944f9cc0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +## [5.3.0](https://github.com/t3-oss/create-t3-app/compare/v5.2.1...v5.3.0) (2022-07-25) + +### Features + +- add typesafe client side env variables ([#209](https://github.com/t3-oss/create-t3-app/issues/209)) ([d4cf879](https://github.com/t3-oss/create-t3-app/commit/d4cf879df811ded012a5f7cb3d91ff0bb95d87ac)) +- improved logo typography ([#238](https://github.com/t3-oss/create-t3-app/issues/238)) ([48c6720](https://github.com/t3-oss/create-t3-app/commit/48c67207da48c901822e214a00285c99bb785fd8)) +- updating tailwind and postcss config's to use .cjs ([#242](https://github.com/t3-oss/create-t3-app/issues/242)) ([5b97367](https://github.com/t3-oss/create-t3-app/commit/5b973670cae729c9e93b6c3c6f0b3e5b48dda904)) + +### Bug Fixes + +- added JSDoc type to clientEnv in all env-schema ([#240](https://github.com/t3-oss/create-t3-app/issues/240)) ([9cb5ebb](https://github.com/t3-oss/create-t3-app/commit/9cb5ebbb2a0147ce15726c7e1a6c6be4065bc2dc)) +- clarify some comments and rename some files in env ([#245](https://github.com/t3-oss/create-t3-app/issues/245)) ([2048783](https://github.com/t3-oss/create-t3-app/commit/2048783bd26f7f0522357a3ab1e74e6f560a221c)) +- remove semicolon in \_app.tsx with next-auth to avoid early return ([1be7713](https://github.com/t3-oss/create-t3-app/commit/1be771393628631b818b46d3f3bf1ca41ad99dce)) +- title being offset when using yarn/pnpm ([c881f00](https://github.com/t3-oss/create-t3-app/commit/c881f00430f606040a448eecc41e779ab0c728a7)) + ### [5.2.1](https://github.com/t3-oss/create-t3-app/compare/v5.2.0...v5.2.1) (2022-07-19) ### Bug Fixes diff --git a/package.json b/package.json index d3ef2da93a..2c4f93d2b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-t3-app", - "version": "5.2.1-next.4", + "version": "5.3.0-next.1", "description": "Create web application with the t3 stack", "author": "Shoubhit Dash ", "license": "MIT", diff --git a/template/addons/env/auth-prisma-schema.mjs b/template/addons/env/auth-prisma-schema.mjs index 5871158c2c..61515a1c6c 100644 --- a/template/addons/env/auth-prisma-schema.mjs +++ b/template/addons/env/auth-prisma-schema.mjs @@ -27,6 +27,7 @@ export const clientSchema = z.object({ * You can't destruct `process.env` as a regular object, so you have to do * it manually here. This is because Next.js evaluates this at build time, * and only used environment variables are included in the build. + * @type {{ [k in keyof z.infer]: z.infer[k] | undefined }} */ export const clientEnv = { // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, diff --git a/template/addons/env/auth-schema.mjs b/template/addons/env/auth-schema.mjs index 173f7a7b0d..bd50f57ff8 100644 --- a/template/addons/env/auth-schema.mjs +++ b/template/addons/env/auth-schema.mjs @@ -25,6 +25,7 @@ export const clientSchema = z.object({ * You can't destruct `process.env` as a regular object, so you have to do * it manually here. This is because Next.js evaluates this at build time, * and only used environment variables are included in the build. + * @type {{ [k in keyof z.infer]: z.infer[k] | undefined }} */ export const clientEnv = { // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, diff --git a/template/addons/env/prisma-schema.mjs b/template/addons/env/prisma-schema.mjs index 4927fa4c4d..db72a631b5 100644 --- a/template/addons/env/prisma-schema.mjs +++ b/template/addons/env/prisma-schema.mjs @@ -23,6 +23,7 @@ export const clientSchema = z.object({ * You can't destruct `process.env` as a regular object, so you have to do * it manually here. This is because Next.js evaluates this at build time, * and only used environment variables are included in the build. + * @type {{ [k in keyof z.infer]: z.infer[k] | undefined }} */ export const clientEnv = { // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR, diff --git a/template/base/src/env/schema.mjs b/template/base/src/env/schema.mjs index cd2d6c1a30..80381624bd 100644 --- a/template/base/src/env/schema.mjs +++ b/template/base/src/env/schema.mjs @@ -22,6 +22,7 @@ export const clientSchema = z.object({ * You can't destruct `process.env` as a regular object, so you have to do * it manually here. This is because Next.js evaluates this at build time, * and only used environment variables are included in the build. + * @type {{ [k in keyof z.infer]: z.infer[k] | undefined }} */ export const clientEnv = { // NEXT_PUBLIC_BAR: process.env.NEXT_PUBLIC_BAR,