├── .eslintrc.js ├── .github ├── dependabot.yml └── workflows │ └── build-test-lint.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── apps ├── api │ ├── .env.example │ ├── .eslintrc.js │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── config │ │ │ ├── config.ts │ │ │ └── env.ts │ │ ├── routes │ │ │ ├── context.ts │ │ │ ├── example.ts │ │ │ ├── health.test.ts │ │ │ ├── health.ts │ │ │ ├── index.ts │ │ │ └── trpc.ts │ │ ├── server.ts │ │ ├── types.ts │ │ └── utils │ │ │ └── testCaller.ts │ └── tsconfig.json └── web │ ├── .env.example │ ├── .eslintignore │ ├── .eslintrc.js │ ├── README.md │ ├── components │ ├── HelloWorld.test.tsx │ └── HelloWorld.tsx │ ├── cypress.config.ts │ ├── cypress │ ├── e2e │ │ └── home.spec.ts │ ├── fixtures │ │ └── example.json │ └── support │ │ ├── commands.ts │ │ └── e2e.ts │ ├── jest.config.js │ ├── jest.setup.js │ ├── next-env.d.ts │ ├── next.config.js │ ├── package.json │ ├── pages │ ├── _app.tsx │ └── index.tsx │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── utils │ └── trpc.ts ├── package.json ├── packages ├── eslint-config-custom-server │ ├── index.js │ └── package.json ├── eslint-config-custom │ ├── index.js │ └── package.json ├── schema │ ├── example.ts │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── tsconfig │ ├── README.md │ ├── base.json │ ├── nextjs.json │ ├── package.json │ └── react-library.json └── ui │ ├── .eslintignore │ ├── .eslintrc.js │ ├── components │ ├── Button.test.tsx │ └── Button.tsx │ ├── index.tsx │ ├── jest.config.ts │ ├── jest.setup.ts │ ├── package.json │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── turbo.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | // This tells ESLint to load the config from the package `eslint-config-custom` 4 | extends: ["custom"], 5 | settings: { 6 | next: { 7 | rootDir: ["apps/*/"], 8 | }, 9 | }, 10 | parserOptions: { 11 | sourceType: "module", 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "npm" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "weekly" 12 | - package-ecosystem: "npm" 13 | directory: "/apps/web" 14 | schedule: 15 | interval: "weekly" 16 | - package-ecosystem: "npm" 17 | directory: "/apps/api" 18 | schedule: 19 | interval: "weekly" 20 | - package-ecosystem: "npm" 21 | directory: "/packages/ui" 22 | schedule: 23 | interval: "weekly" 24 | - package-ecosystem: "npm" 25 | directory: "/packages/schema" 26 | schedule: 27 | interval: "weekly" 28 | -------------------------------------------------------------------------------- /.github/workflows/build-test-lint.yml: -------------------------------------------------------------------------------- 1 | name: Build, Test, and Lint 2 | on: 3 | push: 4 | branches: [dev**] 5 | paths-ignore: 6 | - "**.md" 7 | - .vscode/** 8 | pull_request_target: 9 | types: 10 | - opened 11 | branches: 12 | - main 13 | paths-ignore: 14 | - "**.md" 15 | - .vscode/** 16 | 17 | jobs: 18 | test: 19 | timeout-minutes: 30 20 | if: ${{ github.actor != 'dependabot[bot]' }} 21 | runs-on: ubuntu-latest 22 | env: 23 | TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} 24 | TURBO_TEAM: ${{ secrets.TURBO_TEAM }} 25 | 26 | steps: 27 | - name: Check out repository 28 | uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 2 31 | 32 | - uses: pnpm/action-setup@v2.0.1 33 | with: 34 | version: 7.26.0 35 | 36 | - name: Setup Node 37 | uses: actions/setup-node@v2 38 | with: 39 | node-version: 18.x 40 | cache: "pnpm" 41 | 42 | - name: Install deps 43 | run: pnpm install 44 | 45 | - name: Run build 46 | run: pnpm build 47 | 48 | - name: Run lint 49 | run: pnpm lint 50 | continue-on-error: true 51 | 52 | - name: Run unit tests 53 | run: pnpm test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | #api 4 | dist 5 | .nyc_output 6 | 7 | # dependencies 8 | node_modules 9 | .pnp 10 | .pnp.js 11 | 12 | # testing 13 | coverage 14 | 15 | # next.js 16 | .next/ 17 | out/ 18 | build 19 | 20 | # misc 21 | .DS_Store 22 | *.pem 23 | 24 | # debug 25 | npm-debug.log* 26 | yarn-debug.log* 27 | yarn-error.log* 28 | .pnpm-debug.log* 29 | 30 | # local env files 31 | .env.local 32 | .env.development.local 33 | .env.test.local 34 | .env.production.local 35 | .env.dev 36 | .env.test 37 | .env 38 | 39 | # turbo 40 | .turbo 41 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": ["./apps/api", "./apps/web", "./packages/ui"] 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Brandon Ma 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Fastify tRPC Next.js Starter 2 | 3 | Turborepo setup for using: 4 | - Fastify 5 | - tRPC 6 | - Next.js 7 | - ESLint 8 | 9 | ## Getting Started 10 | ```bash 11 | git clone git@github.com:maybemaby/fastify-trpc-next.git 12 | pnpm i 13 | ``` 14 | 15 | ## What's inside? 16 | 17 | This turborepo uses [pnpm](https://pnpm.io) as a package manager. It includes the following packages/apps: 18 | 19 | ### Apps and Packages 20 | 21 | - `api`: a Fastify + tRPC app 22 | - `web`: a [Next.js](https://nextjs.org/) + tRPC app 23 | - `schema` for sharing zod schemas between the `api` and `web` apps 24 | - `ui`: a stub React component library shared by the `web` application 25 | - `eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`) 26 | - `eslint-config-custom-server`: `eslint` configuration base for server apps 27 | - `tsconfig`: `tsconfig.json`s used throughout the monorepo 28 | 29 | 30 | Each package/app is 100% [TypeScript](https://www.typescriptlang.org/). 31 | 32 | ### Utilities 33 | 34 | This turborepo has some additional tools already setup for you: 35 | 36 | - [TypeScript](https://www.typescriptlang.org/) for static type checking 37 | - [ESLint](https://eslint.org/) for code linting 38 | - [Prettier](https://prettier.io) for code formatting 39 | 40 | ### Build 41 | 42 | To build all apps and packages, run the following command: 43 | 44 | ```bash 45 | pnpm run build 46 | ``` 47 | 48 | ### Develop 49 | 50 | To develop all apps and packages, run the following command: 51 | 52 | ```bash 53 | pnpm run dev 54 | ``` 55 | ### Test:E2E 56 | 57 | To run E2E tests with cypress and the api live 58 | 59 | ```bash 60 | pnpm -w run start 61 | pnpm -w run test:e2e 62 | ``` -------------------------------------------------------------------------------- /apps/api/.env.example: -------------------------------------------------------------------------------- 1 | PORT=5000 -------------------------------------------------------------------------------- /apps/api/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | "custom-server", 5 | "plugin:@typescript-eslint/recommended", 6 | "plugin:@typescript-eslint/recommended-requiring-type-checking", 7 | ], 8 | parser: "@typescript-eslint/parser", 9 | plugins: ["@typescript-eslint"], 10 | rules: { 11 | "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }], 12 | "@typescript-eslint/no-floating-promises": "off", 13 | }, 14 | parserOptions: { 15 | project: "./tsconfig.json", 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /apps/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "server.ts", 6 | "scripts": { 7 | "build": "tsc", 8 | "dev": "dotenv -e .env.dev -- ts-node-dev ./src/server.ts", 9 | "lint": "tsc --noEmit && TIMING=1 eslint \"src/**/*.ts*\"", 10 | "test": "dotenv -e .env.test -- tap src/**/*.test.ts --ts --no-check-coverage", 11 | "start": "dotenv -e .env -- node ./dist/server.js" 12 | }, 13 | "keywords": [], 14 | "author": "maybemaby", 15 | "license": "ISC", 16 | "dependencies": { 17 | "@fastify/cors": "^8.2.0", 18 | "@fastify/helmet": "^10.1.0", 19 | "@fastify/sensible": "^5.2.0", 20 | "@trpc/server": "^10.7.0", 21 | "dotenv-cli": "^6.0.0", 22 | "fastify": "^4.11.0", 23 | "fastify-plugin": "^4.5.0", 24 | "schema": "*", 25 | "superjson": "^1.12.1", 26 | "typescript": "^4.9.4", 27 | "zod": "^3.20.2" 28 | }, 29 | "devDependencies": { 30 | "@types/node": "^18.14.6", 31 | "@types/tap": "^15.0.8", 32 | "@typescript-eslint/eslint-plugin": "^5.47.1", 33 | "@typescript-eslint/parser": "^5.47.1", 34 | "eslint": "^8.31.0", 35 | "eslint-config-custom-server": "*", 36 | "pino-pretty": "^10.0.0", 37 | "tap": "^16.3.4", 38 | "ts-node-dev": "^2.0.0", 39 | "tsconfig": "workspace:*" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /apps/api/src/app.ts: -------------------------------------------------------------------------------- 1 | import fastify, { FastifyServerOptions } from "fastify"; 2 | import sensible from "@fastify/sensible"; 3 | 4 | export const build = (opts?: FastifyServerOptions) => { 5 | const app = fastify(opts); 6 | 7 | app.register(sensible); 8 | return app; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/api/src/config/config.ts: -------------------------------------------------------------------------------- 1 | import { FastifyServerOptions } from "fastify"; 2 | 3 | interface IConfig { 4 | logger: FastifyServerOptions["logger"]; 5 | } 6 | 7 | // Define configs here based on NODE_ENV = "development" | "testing" | "production" 8 | export const config: Record = { 9 | development: { 10 | logger: { 11 | transport: { 12 | target: "pino-pretty", 13 | options: { 14 | colorize: true, 15 | translateTime: "yyyy-mm-dd HH:MM:ss.l", 16 | }, 17 | }, 18 | level: "debug", 19 | }, 20 | }, 21 | production: { 22 | logger: { 23 | serializers: { 24 | req(req) { 25 | return { 26 | method: req.method, 27 | url: req.url, 28 | }; 29 | }, 30 | }, 31 | level: "info", 32 | transport: { 33 | targets: [ 34 | { 35 | target: "pino/file", 36 | level: "info", 37 | options: { 38 | destination: `./logs/production.log`, 39 | mkdir: true, 40 | }, 41 | }, 42 | ], 43 | }, 44 | }, 45 | }, 46 | testing: { logger: false }, 47 | }; 48 | -------------------------------------------------------------------------------- /apps/api/src/config/env.ts: -------------------------------------------------------------------------------- 1 | import z from "zod"; 2 | 3 | export const envSchema = z.object({ 4 | PORT: z.coerce.number().int().default(5000), 5 | NODE_ENV: z.string().default("development"), 6 | HOST: z.string().default("localhost"), 7 | }); 8 | 9 | export const env = envSchema.parse(process.env); 10 | -------------------------------------------------------------------------------- /apps/api/src/routes/context.ts: -------------------------------------------------------------------------------- 1 | import { inferAsyncReturnType } from "@trpc/server"; 2 | import { CreateFastifyContextOptions } from "@trpc/server/adapters/fastify"; 3 | // Reference required for compilation 4 | import type fastify from "fastify"; 5 | 6 | // eslint-disable-next-line @typescript-eslint/require-await 7 | export async function createContextInner() { 8 | return {}; 9 | } 10 | 11 | // eslint-disable-next-line @typescript-eslint/require-await 12 | export async function createContext({ req, res }: CreateFastifyContextOptions) { 13 | const server = req.server; 14 | 15 | return { 16 | fastify: server, 17 | req, 18 | res, 19 | }; 20 | } 21 | 22 | export type Context = inferAsyncReturnType; 23 | export type InnerContext = inferAsyncReturnType; 24 | -------------------------------------------------------------------------------- /apps/api/src/routes/example.ts: -------------------------------------------------------------------------------- 1 | import { router, publicProcedure } from "./trpc"; 2 | import { testSchema } from "schema"; 3 | 4 | export const exampleRouter = router({ 5 | example: publicProcedure.input(testSchema).query(({ ctx, input }) => { 6 | ctx.req.log.info(input, "example"); 7 | return input; 8 | }), 9 | }); 10 | -------------------------------------------------------------------------------- /apps/api/src/routes/health.test.ts: -------------------------------------------------------------------------------- 1 | import t from "tap"; 2 | import { createTestCaller } from "../utils/testCaller"; 3 | 4 | t.test("Health route", (t) => { 5 | t.test("Success", async (t) => { 6 | const caller = await createTestCaller(); 7 | const res = await caller.health.health(); 8 | 9 | t.ok(res.health); 10 | t.match(res, { health: "ok" }); 11 | }); 12 | 13 | t.end(); 14 | }); 15 | -------------------------------------------------------------------------------- /apps/api/src/routes/health.ts: -------------------------------------------------------------------------------- 1 | import { publicProcedure, router } from "./trpc"; 2 | 3 | export const healthRouter = router({ 4 | health: publicProcedure.query(({ ctx }) => { 5 | return { 6 | health: "ok", 7 | }; 8 | }), 9 | }); 10 | -------------------------------------------------------------------------------- /apps/api/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import { exampleRouter } from "./example"; 2 | import { healthRouter } from "./health"; 3 | import { router, _testRouter } from "./trpc"; 4 | 5 | export const appRouter = router({ 6 | health: healthRouter, 7 | example: exampleRouter, 8 | }); 9 | 10 | export const testRouter = _testRouter({ 11 | health: healthRouter, 12 | example: exampleRouter, 13 | }); 14 | 15 | export type AppRouter = typeof appRouter; 16 | -------------------------------------------------------------------------------- /apps/api/src/routes/trpc.ts: -------------------------------------------------------------------------------- 1 | import { initTRPC } from "@trpc/server"; 2 | import SuperJSON from "superjson"; 3 | import { Context, InnerContext } from "./context"; 4 | 5 | const t = initTRPC.context().create({ 6 | transformer: SuperJSON, 7 | errorFormatter({ shape }) { 8 | return shape; 9 | }, 10 | }); 11 | 12 | const testT = initTRPC.context().create({ 13 | transformer: SuperJSON, 14 | errorFormatter({ shape }) { 15 | return shape; 16 | }, 17 | }); 18 | 19 | export const _testRouter = testT.router; 20 | export const router = t.router; 21 | export const publicProcedure = t.procedure; -------------------------------------------------------------------------------- /apps/api/src/server.ts: -------------------------------------------------------------------------------- 1 | import helmet from "@fastify/helmet"; 2 | import cors from "@fastify/cors"; 3 | import { fastifyTRPCPlugin } from "@trpc/server/adapters/fastify"; 4 | import { build } from "./app"; 5 | import { createContext } from "./routes/context"; 6 | import { env } from "./config/env"; 7 | import { config } from "./config/config"; 8 | import { appRouter } from "./routes"; 9 | 10 | const app = build({ 11 | logger: config[env.NODE_ENV].logger, 12 | }); 13 | 14 | app.register(fastifyTRPCPlugin, { 15 | prefix: "/api", 16 | trpcOptions: { 17 | router: appRouter, 18 | createContext, 19 | }, 20 | }); 21 | 22 | app.register(cors, { 23 | origin: "*", 24 | credentials: true, 25 | }); 26 | 27 | app.register(helmet); 28 | 29 | if (env.HOST) { 30 | app.listen( 31 | { 32 | port: env.PORT, 33 | host: env.HOST, 34 | }, 35 | (err, _address) => { 36 | if (err) { 37 | app.log.error(err); 38 | process.exit(1); 39 | } 40 | } 41 | ); 42 | } else { 43 | app.listen( 44 | { 45 | port: env.PORT, 46 | }, 47 | (err, _address) => { 48 | if (err) { 49 | app.log.error(err); 50 | process.exit(1); 51 | } 52 | } 53 | ); 54 | } 55 | -------------------------------------------------------------------------------- /apps/api/src/types.ts: -------------------------------------------------------------------------------- 1 | export type { AppRouter } from "./routes"; 2 | -------------------------------------------------------------------------------- /apps/api/src/utils/testCaller.ts: -------------------------------------------------------------------------------- 1 | import { testRouter } from "../routes"; 2 | import { createContextInner } from "../routes/context"; 3 | 4 | export const createTestCaller = async () => { 5 | const ctx = await createContextInner(); 6 | return testRouter.createCaller(ctx); 7 | }; 8 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, 4 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 5 | "moduleResolution": "node", 6 | "lib": [ 7 | "ESNext" 8 | ] /* Specify library files to be included in the compilation. */, 9 | // "allowJs": true, /* Allow javascript files to be compiled. */ 10 | // "checkJs": true, /* Report errors in .js files. */ 11 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ 12 | "declaration": true /* Generates corresponding '.d.ts' file. */, 13 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 14 | "sourceMap": true /* Generates corresponding '.map' file. */, 15 | // "outFile": "./", /* Concatenate and emit output to single file. */ 16 | "outDir": "./dist" /* Redirect output structure to the directory. */, 17 | "resolveJsonModule": true, 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 30 | "strictNullChecks": true /* Enable strict null checks. */, 31 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 32 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 33 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 34 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 35 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 36 | 37 | /* Additional Checks */ 38 | "noUnusedLocals": false /* Report errors on unused locals. */, 39 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 40 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 41 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 42 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 43 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ 44 | // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ 45 | 46 | /* Module Resolution Options */ 47 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 48 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 51 | // "typeRoots": [], /* List of folders to include type definitions from. */ 52 | // "types": [], /* Type declaration files to be included in compilation. */ 53 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 54 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 55 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 56 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 57 | 58 | /* Source Map Options */ 59 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 61 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 62 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 63 | 64 | /* Experimental Options */ 65 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 66 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 67 | 68 | /* Advanced Options */ 69 | "skipLibCheck": true /* Skip type checking of declaration files. */, 70 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 71 | }, 72 | "extends": "tsconfig/base.json", 73 | "include": ["src/**/*.ts"], 74 | "exclude": ["./node_modules", "dist"] 75 | } 76 | -------------------------------------------------------------------------------- /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_API_URL=http://localhost:5000 2 | NODE_ENV=development -------------------------------------------------------------------------------- /apps/web/.eslintignore: -------------------------------------------------------------------------------- 1 | next.config.js 2 | jest.config.js 3 | jest.setup.js -------------------------------------------------------------------------------- /apps/web/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | plugins: ["@typescript-eslint"], 4 | extends: [ 5 | "custom", 6 | "next/core-web-vitals", 7 | "plugin:@typescript-eslint/recommended", 8 | ], 9 | rules: { 10 | "@typescript-eslint/consistent-type-imports": "warn", 11 | }, 12 | parser: "@typescript-eslint/parser", 13 | parserOptions: { 14 | project: "./tsconfig.json", 15 | sourceType: "module", 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web/README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started 2 | 3 | First, run the development server: 4 | 5 | ```bash 6 | yarn dev 7 | ``` 8 | 9 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 10 | 11 | You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file. 12 | 13 | [API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`. 14 | 15 | The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages. 16 | 17 | ## Learn More 18 | 19 | To learn more about Next.js, take a look at the following resources: 20 | 21 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 22 | - [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial. 23 | 24 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! 25 | 26 | ## Deploy on Vercel 27 | 28 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js. 29 | 30 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. 31 | -------------------------------------------------------------------------------- /apps/web/components/HelloWorld.test.tsx: -------------------------------------------------------------------------------- 1 | import { HelloWorld } from "./HelloWorld"; 2 | import { render, screen } from "@testing-library/react"; 3 | 4 | test("Renders Hello World", () => { 5 | render(); 6 | expect(screen.getByText("Hello World")).toBeTruthy(); 7 | }); 8 | -------------------------------------------------------------------------------- /apps/web/components/HelloWorld.tsx: -------------------------------------------------------------------------------- 1 | export const HelloWorld = () => { 2 | return
Hello World
; 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | 3 | export default defineConfig({ 4 | e2e: { 5 | baseUrl: "http://localhost:3000", 6 | setupNodeEvents(on, config) { 7 | // implement node event listeners here 8 | }, 9 | specPattern: "cypress/e2e/**/*.spec.{js,jsx,ts,tsx}", 10 | }, 11 | video: false, 12 | }); 13 | -------------------------------------------------------------------------------- /apps/web/cypress/e2e/home.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, cy } from "local-cypress"; 2 | 3 | describe("Home", () => { 4 | it("Opens homepage", () => { 5 | cy.visit("/"); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /apps/web/cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /apps/web/cypress/support/commands.ts: -------------------------------------------------------------------------------- 1 | /// 2 | // *********************************************** 3 | // This example commands.ts shows you how to 4 | // create various custom commands and overwrite 5 | // existing commands. 6 | // 7 | // For more comprehensive examples of custom 8 | // commands please read more here: 9 | // https://on.cypress.io/custom-commands 10 | // *********************************************** 11 | // 12 | // 13 | // -- This is a parent command -- 14 | // Cypress.Commands.add('login', (email, password) => { ... }) 15 | // 16 | // 17 | // -- This is a child command -- 18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 19 | // 20 | // 21 | // -- This is a dual command -- 22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 23 | // 24 | // 25 | // -- This will overwrite an existing command -- 26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 27 | // 28 | // declare global { 29 | // namespace Cypress { 30 | // interface Chainable { 31 | // login(email: string, password: string): Chainable 32 | // drag(subject: string, options?: Partial): Chainable 33 | // dismiss(subject: string, options?: Partial): Chainable 34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable 35 | // } 36 | // } 37 | // } -------------------------------------------------------------------------------- /apps/web/cypress/support/e2e.ts: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/e2e.ts is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands' 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') -------------------------------------------------------------------------------- /apps/web/jest.config.js: -------------------------------------------------------------------------------- 1 | const nextJest = require("next/jest"); 2 | 3 | const createJestConfig = nextJest({ 4 | // Provide the path to your Next.js app to load next.config.js and .env files in your test environment 5 | dir: "./", 6 | }); 7 | 8 | // Add any custom config to be passed to Jest 9 | const customJestConfig = { 10 | // Add more setup options before each test is run 11 | // setupFilesAfterEnv: ['/jest.setup.js'], 12 | // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work 13 | moduleDirectories: ["node_modules", "/"], 14 | testEnvironment: "jest-environment-jsdom", 15 | moduleNameMapper: { 16 | "^@components(.*)$": "/src/components/$1", 17 | "^@hooks(.*)$": "/src/hooks/$1", 18 | "^~(.*)$": "/src/$1", 19 | }, 20 | setupFilesAfterEnv: ["@testing-library/jest-dom/extend-expect"], 21 | testRegex: "(/__tests__/.*|(\\.|/)(test))\\.[jt]sx?$", 22 | }; 23 | 24 | // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async 25 | module.exports = createJestConfig(customJestConfig); 26 | -------------------------------------------------------------------------------- /apps/web/jest.setup.js: -------------------------------------------------------------------------------- 1 | // Optional: configure or set up a testing framework before each test. 2 | // If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js` 3 | 4 | // Used for __tests__/testing-library.js 5 | // Learn more: https://github.com/testing-library/jest-dom 6 | import "@testing-library/jest-dom/extend-expect"; 7 | -------------------------------------------------------------------------------- /apps/web/next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/basic-features/typescript for more information. 6 | -------------------------------------------------------------------------------- /apps/web/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import("next").NextConfig} */ 2 | const config = { 3 | reactStrictMode: true, 4 | swcMinify: true, 5 | transpilePackages: ["ui"], 6 | typescript: { 7 | ignoreBuildErrors: true, 8 | }, 9 | }; 10 | 11 | const withBundleAnalyzer = require("@next/bundle-analyzer")({ 12 | enabled: process.env.ANALYZE === "true", 13 | }); 14 | 15 | module.exports = withBundleAnalyzer(config); 16 | -------------------------------------------------------------------------------- /apps/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "web", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "test": "jest --passWithNoTests", 9 | "test:w": "jest --watch", 10 | "test:e2e": "cypress run -b chrome --headed", 11 | "cypress:open": "cypress open", 12 | "build:analyze": "ANALYZE=true next build", 13 | "start": "next start", 14 | "lint": "next lint" 15 | }, 16 | "dependencies": { 17 | "@tanstack/react-query": "^4.20.4", 18 | "@trpc/client": "^10.7.0", 19 | "@trpc/next": "^10.7.0", 20 | "@trpc/react-query": "^10.7.0", 21 | "@trpc/server": "^10.7.0", 22 | "next": "^13.2.3", 23 | "react": "18.2.0", 24 | "react-dom": "18.2.0", 25 | "superjson": "^1.12.1", 26 | "ui": "workspace:*" 27 | }, 28 | "devDependencies": { 29 | "@babel/core": "^7.0.0", 30 | "@next/bundle-analyzer": "^13.2.3", 31 | "@testing-library/dom": "^9.0.1", 32 | "@testing-library/jest-dom": "^5.16.5", 33 | "@testing-library/react": "^14.0.0", 34 | "@testing-library/user-event": "^14.4.3", 35 | "@types/jest": "^29.2.5", 36 | "@types/node": "^18.14.6", 37 | "@types/react": "^18.0.22", 38 | "@types/react-dom": "^18.0.7", 39 | "@typescript-eslint/eslint-plugin": "^5.47.1", 40 | "@typescript-eslint/parser": "^5.47.1", 41 | "api": "workspace:*", 42 | "cypress": "^12.3.0", 43 | "eslint": "8.35.0", 44 | "eslint-config-custom": "workspace:*", 45 | "eslint-plugin-testing-library": "^5.10.1", 46 | "fastify": "^4.11.0", 47 | "jest": "^29.4.2", 48 | "jest-environment-jsdom": "^29.4.2", 49 | "local-cypress": "^1.2.6", 50 | "tsconfig": "workspace:*", 51 | "typescript": "^4.5.3" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /apps/web/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import type { AppType } from "next/app"; 2 | import { trpc } from "../utils/trpc"; 3 | 4 | const MyApp: AppType = ({ Component, pageProps }) => { 5 | return ; 6 | }; 7 | export default trpc.withTRPC(MyApp); 8 | -------------------------------------------------------------------------------- /apps/web/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "ui"; 2 | import { trpc } from "../utils/trpc"; 3 | 4 | export default function Web() { 5 | const health = trpc.health.health.useQuery(); 6 | 7 | return ( 8 |
9 | {health.data &&

TRPC Health: {health.data.health}

} 10 |

Web

11 |
13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "isolatedModules": true, 4 | "types": ["jest"] 5 | }, 6 | "extends": "tsconfig/nextjs.json", 7 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 8 | "exclude": ["node_modules", "**/*.spec.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/web/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["**/*.spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /apps/web/utils/trpc.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable turbo/no-undeclared-env-vars */ 2 | import { httpBatchLink, loggerLink } from "@trpc/client"; 3 | import type { inferRouterInputs } from "@trpc/server"; 4 | import type { inferRouterOutputs } from "@trpc/server"; 5 | import { createTRPCNext } from "@trpc/next"; 6 | import superjson from "superjson"; 7 | import type fastify from "fastify"; 8 | import type { AppRouter } from "api/src/types"; 9 | 10 | function getBaseUrl() { 11 | if (typeof window !== "undefined") 12 | // browser should use relative path 13 | return process.env.NEXT_PUBLIC_API_URL ?? "http://localhost:5000"; 14 | if (process.env.VERCEL_URL) 15 | // reference for vercel.com 16 | return `https://${process.env.VERCEL_URL}`; 17 | if (process.env.RENDER_INTERNAL_HOSTNAME) 18 | // reference for render.com 19 | return `http://${process.env.RENDER_INTERNAL_HOSTNAME}:${process.env.PORT}`; 20 | // assume localhost 21 | return `http://localhost:${process.env.PORT ?? 5000}`; 22 | } 23 | 24 | export const trpc = createTRPCNext({ 25 | config({ ctx }) { 26 | return { 27 | queryClientConfig: { 28 | defaultOptions: { 29 | queries: { 30 | refetchOnWindowFocus: false, 31 | }, 32 | }, 33 | }, 34 | transformer: superjson, 35 | links: [ 36 | loggerLink({ 37 | enabled: (opts) => 38 | process.env.NODE_ENV === "development" || 39 | (opts.direction === "down" && opts.result instanceof Error), 40 | }), 41 | httpBatchLink({ url: `${getBaseUrl()}/api` }), 42 | ], 43 | }; 44 | }, 45 | ssr: false, 46 | }); 47 | 48 | export type RouterInputs = inferRouterInputs; 49 | export type RouterOutputs = inferRouterOutputs; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fastify-trpc-next", 3 | "version": "0.0.0", 4 | "repository": "https://github.com/maybemaby/fastify-trpc-next", 5 | "private": true, 6 | "workspaces": [ 7 | "apps/*", 8 | "packages/*" 9 | ], 10 | "scripts": { 11 | "build": "turbo run build", 12 | "dev": "turbo run dev --parallel --force", 13 | "lint": "turbo run lint --filter=!schema --filter=!ui", 14 | "test": "turbo run test", 15 | "test:e2e": "turbo run test:e2e", 16 | "format": "prettier --write \"**/*.{ts,tsx,md}\"", 17 | "start": "turbo run start" 18 | }, 19 | "devDependencies": { 20 | "eslint-config-custom": "workspace:*", 21 | "prettier": "^2.8.4", 22 | "turbo": "^1.8.3" 23 | }, 24 | "engines": { 25 | "node": ">=14.0.0" 26 | }, 27 | "packageManager": "pnpm@7.20.0" 28 | } 29 | -------------------------------------------------------------------------------- /packages/eslint-config-custom-server/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["eslint:recommended", "turbo"], 3 | env: { 4 | node: true, 5 | es6: true, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/eslint-config-custom-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-custom-server", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "eslint": "^8.35.0", 8 | "eslint-config-turbo": "latest" 9 | }, 10 | "devDependencies": { 11 | "typescript": "^4.7.4" 12 | }, 13 | "publishConfig": { 14 | "access": "public" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/eslint-config-custom/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ["next", "turbo", "prettier"], 3 | rules: { 4 | "@next/next/no-html-link-for-pages": "off", 5 | "react/jsx-key": "off", 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/eslint-config-custom/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint-config-custom", 3 | "version": "0.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "eslint": "^8.35.0", 8 | "eslint-config-next": "13.1.6", 9 | "eslint-config-prettier": "^8.3.0", 10 | "eslint-plugin-react": "7.32.2", 11 | "eslint-config-turbo": "latest" 12 | }, 13 | "devDependencies": { 14 | "typescript": "^4.7.4" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/schema/example.ts: -------------------------------------------------------------------------------- 1 | import z from "zod"; 2 | 3 | export const testSchema = z.object({ 4 | test: z.string(), 5 | }); 6 | -------------------------------------------------------------------------------- /packages/schema/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./example"; -------------------------------------------------------------------------------- /packages/schema/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "schema", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "dist/index.js", 6 | "src": "index.ts", 7 | "scripts": { 8 | "build": "tsc" 9 | }, 10 | "keywords": [], 11 | "author": "maybemaby", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "typescript": "^4.9.4", 15 | "tsconfig": "workspace:*" 16 | }, 17 | "dependencies": { 18 | "zod": "^3.20.2" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/schema/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, 4 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 5 | "moduleResolution": "node", 6 | "lib": [ 7 | "ESNext" 8 | ] /* Specify library files to be included in the compilation. */, 9 | "outDir": "dist", 10 | "rootDir": ".", 11 | "declaration": true, 12 | "declarationMap": true, 13 | "sourceMap": true, 14 | "composite": true 15 | }, 16 | "include": ["**/*.ts"], 17 | "exclude": ["node_modules", "dist"], 18 | "extends": "tsconfig/base.json" 19 | } 20 | -------------------------------------------------------------------------------- /packages/tsconfig/README.md: -------------------------------------------------------------------------------- 1 | # `tsconfig` 2 | 3 | These are base shared `tsconfig.json`s from which all other `tsconfig.json`'s inherit from. 4 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "inlineSources": false, 11 | "isolatedModules": true, 12 | "moduleResolution": "node", 13 | "noUnusedLocals": false, 14 | "noUnusedParameters": false, 15 | "preserveWatchOutput": true, 16 | "skipLibCheck": true, 17 | "strict": true 18 | }, 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/tsconfig/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "target": "es5", 7 | "lib": ["dom", "dom.iterable", "esnext"], 8 | "allowJs": true, 9 | "skipLibCheck": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "noEmit": true, 13 | "incremental": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "jsx": "preserve" 19 | }, 20 | "include": ["src", "next-env.d.ts"], 21 | "exclude": ["node_modules"] 22 | } 23 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tsconfig", 3 | "version": "0.0.0", 4 | "private": true, 5 | "files": [ 6 | "base.json", 7 | "nextjs.json", 8 | "react-library.json" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /packages/tsconfig/react-library.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React Library", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx", 7 | "lib": ["ES2015"], 8 | "module": "ESNext", 9 | "target": "es6" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/ui/.eslintignore: -------------------------------------------------------------------------------- 1 | jest.config.ts 2 | jest.setup.ts -------------------------------------------------------------------------------- /packages/ui/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | plugins: ["@typescript-eslint"], 4 | extends: [ 5 | "custom", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react/recommended", 8 | "plugin:jsx-a11y/recommended", 9 | ], 10 | rules: { 11 | "@typescript-eslint/consistent-type-imports": "warn", 12 | "react/react-in-jsx-scope": "off", 13 | }, 14 | parser: "@typescript-eslint/parser", 15 | parserOptions: { 16 | project: "./tsconfig.json", 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /packages/ui/components/Button.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from "@testing-library/react"; 2 | import { Button } from "./Button"; 3 | 4 | describe("Button", () => { 5 | test("Renders Button", () => { 6 | render(); 7 | expect(screen.getByRole("button")).toBeTruthy(); 8 | }) 9 | }) -------------------------------------------------------------------------------- /packages/ui/components/Button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export const Button = () => { 3 | return ; 4 | }; 5 | -------------------------------------------------------------------------------- /packages/ui/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | export * from "./components/Button"; 3 | -------------------------------------------------------------------------------- /packages/ui/jest.config.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | testEnvironment: "jsdom", 3 | setupFilesAfterEnv: ["./jest.setup.ts"], 4 | moduleFileExtensions: ["ts", "tsx", "js"], 5 | testPathIgnorePatterns: ["node_modules/"], 6 | transform: { 7 | "^.+\\.tsx?$": "ts-jest", 8 | }, 9 | testMatch: ["**/*.test.(ts|tsx)"], 10 | moduleNameMapper: { 11 | // Mocks out all these file formats when tests are run. 12 | "\\.(jpg|ico|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": 13 | "identity-obj-proxy", 14 | "\\.(css|less|scss|sass)$": "/__mocks__/styles.js", 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/ui/jest.setup.ts: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "0.0.0", 4 | "main": "./index.tsx", 5 | "types": "./index.tsx", 6 | "license": "MIT", 7 | "scripts": { 8 | "lint": "eslint *.ts*", 9 | "test": "jest", 10 | "test:w": "jest --watch" 11 | }, 12 | "devDependencies": { 13 | "@types/react": "^18.0.17", 14 | "@types/react-dom": "^18.0.6", 15 | "eslint": "^8.35.0", 16 | "eslint-config-custom": "workspace:*", 17 | "eslint-plugin-react": "^7.32.1", 18 | "@typescript-eslint/eslint-plugin": "^5.47.1", 19 | "@typescript-eslint/parser": "^5.47.1", 20 | "react": "^18.2.0", 21 | "tsconfig": "workspace:*", 22 | "typescript": "^4.5.2", 23 | "ts-jest": "^29.0.3", 24 | "jest": "^29.3.1", 25 | "jest-environment-jsdom": "^29.3.1", 26 | "@testing-library/dom": "^9.0.1", 27 | "@testing-library/jest-dom": "^5.16.5", 28 | "@testing-library/react": "^14.0.0", 29 | "@testing-library/user-event": "^14.4.3", 30 | "@types/jest": "^29.2.5" 31 | }, 32 | "peerDependencies": { 33 | "react-dom": "18.2.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tsconfig/react-library.json", 3 | "include": ["."], 4 | "exclude": ["dist", "build", "node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | 4 | "pipeline": { 5 | "build": { 6 | "env": ["VERCEL_URL", "RENDER_INTERNAL_HOSTNAME", "NEXT_PUBLIC_PORT"], 7 | "dependsOn": ["^build"], 8 | "outputs": ["dist/**", ".next/**"] 9 | }, 10 | "lint": { 11 | "outputs": [] 12 | }, 13 | "dev": { 14 | "persistent": true, 15 | "cache": false 16 | }, 17 | "start": { 18 | "persistent": true, 19 | "dependsOn": ["^build"] 20 | }, 21 | "test": { 22 | "inputs": [ 23 | "packages/ui/**/*.tsx", 24 | "packages/ui/**/*.ts", 25 | "apps/web/components/**/*.tsx", 26 | "apps/web/components/**/*.ts" 27 | ] 28 | }, 29 | "test:e2e": {} 30 | } 31 | } 32 | --------------------------------------------------------------------------------