project setup
This commit is contained in:
3
.env.example
Normal file
3
.env.example
Normal file
@@ -0,0 +1,3 @@
|
||||
# Drizzle
|
||||
DATABASE_URL='mysql://YOUR_MYSQL_URL_HERE?ssl={"rejectUnauthorized":true}'
|
||||
|
||||
39
.eslintrc.cjs
Normal file
39
.eslintrc.cjs
Normal file
@@ -0,0 +1,39 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
const config = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: true,
|
||||
},
|
||||
plugins: ["@typescript-eslint"],
|
||||
extends: [
|
||||
"next/core-web-vitals",
|
||||
"plugin:@typescript-eslint/recommended-type-checked",
|
||||
"plugin:@typescript-eslint/stylistic-type-checked",
|
||||
],
|
||||
rules: {
|
||||
// These opinionated rules are enabled in stylistic-type-checked above.
|
||||
// Feel free to reconfigure them to your own preference.
|
||||
"@typescript-eslint/array-type": "off",
|
||||
"@typescript-eslint/consistent-type-definitions": "off",
|
||||
|
||||
"@typescript-eslint/consistent-type-imports": [
|
||||
"warn",
|
||||
{
|
||||
prefer: "type-imports",
|
||||
fixStyle: "inline-type-imports",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
|
||||
"@typescript-eslint/require-await": "off",
|
||||
"@typescript-eslint/no-misused-promises": [
|
||||
"error",
|
||||
{
|
||||
checksVoidReturn: { attributes: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# Testing
|
||||
/coverage
|
||||
|
||||
# Database
|
||||
/prisma/db.sqlite
|
||||
/prisma/db.sqlite-journal
|
||||
|
||||
# Next.js
|
||||
/.next/
|
||||
/out/
|
||||
next-env.d.ts
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# Debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Local env files
|
||||
.env
|
||||
.env*.local
|
||||
|
||||
# Vercel
|
||||
.vercel
|
||||
|
||||
# Typescript
|
||||
*.tsbuildinfo
|
||||
18
.prettierrc.cjs
Normal file
18
.prettierrc.cjs
Normal file
@@ -0,0 +1,18 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import("prettier").Options & import("prettier-plugin-tailwindcss").PluginOptions} */
|
||||
const config = {
|
||||
plugins: ["prettier-plugin-tailwindcss"],
|
||||
arrowParens: "avoid",
|
||||
bracketSpacing: true,
|
||||
quoteProps: "consistent",
|
||||
jsxSingleQuote: false,
|
||||
printWidth: 80,
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
tabWidth: 2,
|
||||
trailingComma: "all",
|
||||
useTabs: false,
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
16
drizzle.config.ts
Normal file
16
drizzle.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// @ts-check
|
||||
|
||||
import { type Config } from "drizzle-kit";
|
||||
|
||||
import { env } from "~/env";
|
||||
|
||||
const config: Config = {
|
||||
schema: "./src/server/db/schema.ts",
|
||||
driver: "mysql2",
|
||||
dbCredentials: {
|
||||
uri: env.DATABASE_URL,
|
||||
},
|
||||
tablesFilter: ["me_*"],
|
||||
};
|
||||
|
||||
export default config;
|
||||
15
next.config.js
Normal file
15
next.config.js
Normal file
@@ -0,0 +1,15 @@
|
||||
// @ts-check
|
||||
|
||||
await import("./src/env.js");
|
||||
|
||||
/** @type {import("next").NextConfig} */
|
||||
const config = {
|
||||
reactStrictMode: true,
|
||||
|
||||
i18n: {
|
||||
locales: ["en"],
|
||||
defaultLocale: "en",
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
52
package.json
Normal file
52
package.json
Normal file
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "pihkaal.me",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "next build",
|
||||
"db:push": "drizzle-kit push:mysql",
|
||||
"db:studio": "drizzle-kit studio",
|
||||
"dev": "next dev",
|
||||
"lint": "next lint --cache --fix",
|
||||
"start": "next start",
|
||||
"format": "prettier --cache --write '**/*.{md,json,css,scss,js,mjs,cjs,ts,tsx}'"
|
||||
},
|
||||
"dependencies": {
|
||||
"@planetscale/database": "^1.11.0",
|
||||
"@t3-oss/env-nextjs": "^0.7.1",
|
||||
"@tanstack/react-query": "^4.36.1",
|
||||
"@trpc/client": "^10.43.6",
|
||||
"@trpc/next": "^10.43.6",
|
||||
"@trpc/react-query": "^10.43.6",
|
||||
"@trpc/server": "^10.43.6",
|
||||
"drizzle-orm": "^0.29.3",
|
||||
"next": "^14.0.4",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"superjson": "^2.2.1",
|
||||
"zod": "^3.22.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/eslint": "^8.44.7",
|
||||
"@types/node": "^18.17.0",
|
||||
"@types/react": "^18.2.37",
|
||||
"@types/react-dom": "^18.2.15",
|
||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||
"@typescript-eslint/parser": "^6.11.0",
|
||||
"autoprefixer": "^10.4.14",
|
||||
"drizzle-kit": "^0.20.9",
|
||||
"eslint": "^8.54.0",
|
||||
"eslint-config-next": "^14.0.4",
|
||||
"mysql2": "^3.6.1",
|
||||
"postcss": "^8.4.31",
|
||||
"prettier": "^3.1.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||
"tailwindcss": "^3.3.5",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"ct3aMetadata": {
|
||||
"initVersion": "7.25.1"
|
||||
},
|
||||
"packageManager": "pnpm@8.14.1"
|
||||
}
|
||||
3967
pnpm-lock.yaml
generated
Normal file
3967
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
10
postcss.config.cjs
Normal file
10
postcss.config.cjs
Normal file
@@ -0,0 +1,10 @@
|
||||
// @ts-check
|
||||
|
||||
const config = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
26
src/env.js
Normal file
26
src/env.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createEnv } from "@t3-oss/env-nextjs";
|
||||
import { z } from "zod";
|
||||
|
||||
export const env = createEnv({
|
||||
server: {
|
||||
DATABASE_URL: z
|
||||
.string()
|
||||
.url()
|
||||
.refine(
|
||||
str => !str.includes("YOUR_MYSQL_URL_HERE"),
|
||||
"You forgot to change the default URL",
|
||||
),
|
||||
NODE_ENV: z
|
||||
.enum(["development", "test", "production"])
|
||||
.default("development"),
|
||||
},
|
||||
|
||||
client: {},
|
||||
|
||||
runtimeEnv: {
|
||||
DATABASE_URL: process.env.DATABASE_URL,
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
},
|
||||
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
||||
emptyStringAsUndefined: true,
|
||||
});
|
||||
11
src/pages/_app.tsx
Normal file
11
src/pages/_app.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import { type AppType } from "next/app";
|
||||
|
||||
import { api } from "~/utils/api";
|
||||
|
||||
import "~/styles/globals.css";
|
||||
|
||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
||||
return <Component {...pageProps} />;
|
||||
};
|
||||
|
||||
export default api.withTRPC(MyApp);
|
||||
18
src/pages/api/trpc/[trpc].ts
Normal file
18
src/pages/api/trpc/[trpc].ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { createNextApiHandler } from "@trpc/server/adapters/next";
|
||||
|
||||
import { env } from "~/env";
|
||||
import { appRouter } from "~/server/api/root";
|
||||
import { createTRPCContext } from "~/server/api/trpc";
|
||||
|
||||
export default createNextApiHandler({
|
||||
router: appRouter,
|
||||
createContext: createTRPCContext,
|
||||
onError:
|
||||
env.NODE_ENV === "development"
|
||||
? ({ path, error }) => {
|
||||
console.error(
|
||||
`❌ tRPC failed on ${path ?? "<no-path>"}: ${error.message}`,
|
||||
);
|
||||
}
|
||||
: undefined,
|
||||
});
|
||||
12
src/pages/index.tsx
Normal file
12
src/pages/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
import Head from "next/head";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>pihkaal</title>
|
||||
</Head>
|
||||
<main></main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
5
src/server/api/root.ts
Normal file
5
src/server/api/root.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { createTRPCRouter } from "~/server/api/trpc";
|
||||
|
||||
export const appRouter = createTRPCRouter({});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
||||
48
src/server/api/trpc.ts
Normal file
48
src/server/api/trpc.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { initTRPC } from "@trpc/server";
|
||||
import { type CreateNextContextOptions } from "@trpc/server/adapters/next";
|
||||
import superjson from "superjson";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
import { db } from "~/server/db";
|
||||
|
||||
/**
|
||||
* 1. CONTEXT
|
||||
*/
|
||||
|
||||
type CreateContextOptions = Record<string, never>;
|
||||
|
||||
const createInnerTRPCContext = (_opts: CreateContextOptions) => {
|
||||
return {
|
||||
db,
|
||||
};
|
||||
};
|
||||
|
||||
export const createTRPCContext = (_opts: CreateNextContextOptions) => {
|
||||
return createInnerTRPCContext({});
|
||||
};
|
||||
|
||||
/**
|
||||
* 2. INITIALIZATION
|
||||
*/
|
||||
|
||||
const t = initTRPC.context<typeof createTRPCContext>().create({
|
||||
transformer: superjson,
|
||||
errorFormatter({ shape, error }) {
|
||||
return {
|
||||
...shape,
|
||||
data: {
|
||||
...shape.data,
|
||||
zodError:
|
||||
error.cause instanceof ZodError ? error.cause.flatten() : null,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
|
||||
*/
|
||||
|
||||
export const createTRPCRouter = t.router;
|
||||
|
||||
export const publicProcedure = t.procedure;
|
||||
12
src/server/db/index.ts
Normal file
12
src/server/db/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Client } from "@planetscale/database";
|
||||
import { drizzle } from "drizzle-orm/planetscale-serverless";
|
||||
|
||||
import { env } from "~/env";
|
||||
import * as schema from "./schema";
|
||||
|
||||
export const db = drizzle(
|
||||
new Client({
|
||||
url: env.DATABASE_URL,
|
||||
}).connection(),
|
||||
{ schema },
|
||||
);
|
||||
3
src/server/db/schema.ts
Normal file
3
src/server/db/schema.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import { mysqlTableCreator } from "drizzle-orm/mysql-core";
|
||||
|
||||
export const mysqlTable = mysqlTableCreator(name => `me_${name}`);
|
||||
3
src/styles/globals.css
Normal file
3
src/styles/globals.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
36
src/utils/api.ts
Normal file
36
src/utils/api.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { httpBatchLink, loggerLink } from "@trpc/client";
|
||||
import { createTRPCNext } from "@trpc/next";
|
||||
import { type inferRouterInputs, type inferRouterOutputs } from "@trpc/server";
|
||||
import superjson from "superjson";
|
||||
|
||||
import { type AppRouter } from "~/server/api/root";
|
||||
|
||||
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 api = createTRPCNext<AppRouter>({
|
||||
config() {
|
||||
return {
|
||||
transformer: superjson,
|
||||
|
||||
links: [
|
||||
loggerLink({
|
||||
enabled: opts =>
|
||||
process.env.NODE_ENV === "development" ||
|
||||
(opts.direction === "down" && opts.result instanceof Error),
|
||||
}),
|
||||
httpBatchLink({
|
||||
url: `${getBaseUrl()}/api/trpc`,
|
||||
}),
|
||||
],
|
||||
};
|
||||
},
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
export type RouterInputs = inferRouterInputs<AppRouter>;
|
||||
|
||||
export type RouterOutputs = inferRouterOutputs<AppRouter>;
|
||||
16
tailwind.config.ts
Normal file
16
tailwind.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { type Config } from "tailwindcss";
|
||||
import { fontFamily } from "tailwindcss/defaultTheme";
|
||||
|
||||
const config: Config = {
|
||||
content: ["./src/**/*.tsx"],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ["var(--font-sans)", ...fontFamily.sans],
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
export default config;
|
||||
42
tsconfig.json
Normal file
42
tsconfig.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Base Options: */
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"target": "es2022",
|
||||
"allowJs": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleDetection": "force",
|
||||
"isolatedModules": true,
|
||||
|
||||
/* Strictness */
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"checkJs": true,
|
||||
|
||||
/* Bundled projects */
|
||||
"lib": ["dom", "dom.iterable", "ES2022"],
|
||||
"noEmit": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"jsx": "preserve",
|
||||
"plugins": [{ "name": "next" }],
|
||||
"incremental": true,
|
||||
|
||||
/* Path Aliases */
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
".eslintrc.cjs",
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.cjs",
|
||||
"**/*.js",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user