patra-turbo-template 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +81 -0
  2. package/cli.js +47 -0
  3. package/package.json +30 -0
  4. package/template/README.md +20 -0
  5. package/template/apps/http-server/README.md +1 -0
  6. package/template/apps/http-server/index.ts +35 -0
  7. package/template/apps/http-server/package.json +22 -0
  8. package/template/apps/web/README.md +1 -0
  9. package/template/apps/web/app/favicon.ico +0 -0
  10. package/template/apps/web/app/fonts/GeistMonoVF.woff +0 -0
  11. package/template/apps/web/app/fonts/GeistVF.woff +0 -0
  12. package/template/apps/web/app/globals.css +1 -0
  13. package/template/apps/web/app/layout.tsx +31 -0
  14. package/template/apps/web/app/page.module.css +186 -0
  15. package/template/apps/web/app/page.tsx +25 -0
  16. package/template/apps/web/eslint.config.js +4 -0
  17. package/template/apps/web/next.config.js +4 -0
  18. package/template/apps/web/package.json +28 -0
  19. package/template/apps/web/postcss.config.mjs +7 -0
  20. package/template/apps/web/tsconfig.json +20 -0
  21. package/template/apps/ws-server/README.md +1 -0
  22. package/template/apps/ws-server/index.ts +25 -0
  23. package/template/apps/ws-server/package.json +27 -0
  24. package/template/apps/ws-server/tsconfig.json +29 -0
  25. package/template/bun.lock +1163 -0
  26. package/template/package.json +30 -0
  27. package/template/packages/db/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +111 -0
  28. package/template/packages/db/README.md +15 -0
  29. package/template/packages/db/index.ts +19 -0
  30. package/template/packages/db/package.json +22 -0
  31. package/template/packages/db/prisma/schema.prisma +20 -0
  32. package/template/packages/db/prisma.config.ts +14 -0
  33. package/template/packages/db/tsconfig.json +29 -0
  34. package/template/packages/eslint-config/README.md +3 -0
  35. package/template/packages/eslint-config/base.js +32 -0
  36. package/template/packages/eslint-config/next.js +57 -0
  37. package/template/packages/eslint-config/package.json +24 -0
  38. package/template/packages/eslint-config/react-internal.js +39 -0
  39. package/template/packages/typescript-config/base.json +19 -0
  40. package/template/packages/typescript-config/nextjs.json +12 -0
  41. package/template/packages/typescript-config/package.json +14 -0
  42. package/template/packages/typescript-config/react-library.json +7 -0
  43. package/template/packages/ui/eslint.config.mjs +4 -0
  44. package/template/packages/ui/package.json +26 -0
  45. package/template/packages/ui/src/button.tsx +20 -0
  46. package/template/packages/ui/src/card.tsx +27 -0
  47. package/template/packages/ui/src/code.tsx +11 -0
  48. package/template/packages/ui/tsconfig.json +8 -0
  49. package/template/turbo.json +21 -0
package/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # patra-turbo-template
2
+
3
+ Scaffold a new Turborepo monorepo from the turbo-template (Next.js, Express, WebSocket, Prisma, Tailwind).
4
+
5
+ **Layout:** Both the npm package and the template live under `template/`:
6
+ - `template/create-turbo-template/` – this package (publish to npm)
7
+ - `template/turbo-template/` – the monorepo template (source for scaffolding)
8
+
9
+ ## What to do next
10
+
11
+ 1. **Bundle the template into the package** (so it’s included when you publish):
12
+ ```bash
13
+ cd template/create-turbo-template
14
+ bun run copy-template
15
+ ```
16
+ This copies `../turbo-template` into `./template` (creates `template/create-turbo-template/template/` with the app).
17
+
18
+ 2. **Test locally** (from any empty folder):
19
+ ```bash
20
+ node path/to/template/create-turbo-template/cli.js my-test-app
21
+ cd my-test-app && bun install && bun run dev
22
+ ```
23
+
24
+ 3. **Publish to npm**
25
+ Open a terminal, go into this package folder, then run:
26
+ ```bash
27
+ cd path/to/CI_CD/template/create-turbo-template
28
+ npm login
29
+ npm publish
30
+ ```
31
+ (Replace `path/to/CI_CD` with your actual path, e.g. `C:\Users\patra\Desktop\Devops_prac\CI_CD` on Windows.)
32
+ `prepublishOnly` will run `copy-template` again before packing, so the tarball always has an up-to-date `template/`.
33
+
34
+ ## Usage (after publish)
35
+
36
+ ```bash
37
+ npx create-turbo-template my-app
38
+ cd my-app
39
+ bun install
40
+ bun run dev
41
+ ```
42
+
43
+ Or with npm:
44
+
45
+ ```bash
46
+ npm create turbo-template@latest my-app
47
+ cd my-app
48
+ bun install
49
+ bun run dev
50
+ ```
51
+
52
+ ## Publishing to npm
53
+
54
+ 1. **One-time: copy the template into the package**
55
+ ```bash
56
+ cd create-turbo-template
57
+ bun run copy-template
58
+ ```
59
+ This copies `../turbo-template` into `./template` (excluding node_modules, .git, etc.).
60
+
61
+ 2. **Publish**
62
+ - If you don’t have an npm account: [npmjs.com](https://www.npmjs.com/signup)
63
+ - npm requires **two-factor authentication (2FA)** to publish. Enable it at [npmjs.com → Account → Security](https://www.npmjs.com/settings/~/account).
64
+ - Login: `npm login`
65
+ - Publish (choose one):
66
+ - **Unscoped (name must be free):** `npm publish`
67
+ - **Scoped (e.g. @yourusername/turbo-template):** change `"name": "create-turbo-template"` to `"name": "@yourusername/create-turbo-template"` in package.json, then `npm publish --access public`
68
+
69
+ 3. **Later: after changing turbo-template**
70
+ - Run `bun run copy-template` again in `create-turbo-template`
71
+ - Bump version in package.json (e.g. `1.0.1`)
72
+ - Run `npm publish`
73
+
74
+ ## What gets created
75
+
76
+ - `apps/web` – Next.js (port 3000)
77
+ - `apps/http-server` – Express API (port 3002)
78
+ - `apps/ws-server` – WebSocket server (port 3001)
79
+ - `packages/db` – Prisma + PostgreSQL
80
+ - `packages/ui` – Shared React components
81
+ - `packages/eslint-config`, `packages/typescript-config` – Shared configs
package/cli.js ADDED
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { mkdir, cp, readdir, readFile, writeFile } from "node:fs/promises";
4
+ import { join, dirname } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ const __dirname = dirname(fileURLToPath(import.meta.url));
8
+ const TEMPLATE_DIR = join(__dirname, "template");
9
+
10
+ async function copyRecursive(src, dest) {
11
+ await mkdir(dest, { recursive: true });
12
+ const entries = await readdir(src, { withFileTypes: true });
13
+ for (const entry of entries) {
14
+ const srcPath = join(src, entry.name);
15
+ const destPath = join(dest, entry.name);
16
+ if (entry.isDirectory()) {
17
+ await copyRecursive(srcPath, destPath);
18
+ } else {
19
+ await cp(srcPath, destPath);
20
+ }
21
+ }
22
+ }
23
+
24
+ async function main() {
25
+ const projectName = process.argv[2] || "my-turbo-app";
26
+ const targetDir = join(process.cwd(), projectName);
27
+
28
+ console.log(`Creating ${projectName} from turbo-template...`);
29
+
30
+ try {
31
+ await copyRecursive(TEMPLATE_DIR, targetDir);
32
+ const pkgPath = join(targetDir, "package.json");
33
+ const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
34
+ pkg.name = projectName;
35
+ await writeFile(pkgPath, JSON.stringify(pkg, null, 2));
36
+ console.log(`Done. Next steps:\n cd ${projectName}\n bun install\n bun run dev`);
37
+ } catch (err) {
38
+ if (err.code === "ENOENT" && err.path === TEMPLATE_DIR) {
39
+ console.error("Error: template folder not found. Run 'npm run copy-template' in the package directory before publishing.");
40
+ } else {
41
+ console.error(err);
42
+ }
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ main();
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "patra-turbo-template",
3
+ "version": "1.0.0",
4
+ "description": "Scaffold a new project from the turbo-template monorepo (Next.js, Express, WebSocket, Prisma, Tailwind)",
5
+ "type": "module",
6
+ "bin": {
7
+ "create-turbo-template": "./cli.js"
8
+ },
9
+ "files": [
10
+ "cli.js",
11
+ "template"
12
+ ],
13
+ "scripts": {
14
+ "copy-template": "node copy-template.js",
15
+ "prepublishOnly": "node copy-template.js"
16
+ },
17
+ "keywords": [
18
+ "turborepo",
19
+ "monorepo",
20
+ "template",
21
+ "next.js",
22
+ "prisma",
23
+ "express",
24
+ "scaffold"
25
+ ],
26
+ "license": "MIT",
27
+ "engines": {
28
+ "node": ">=18"
29
+ }
30
+ }
@@ -0,0 +1,20 @@
1
+ # Turborepo-template
2
+ This is a self managed turborepo for use in future and/or current projects
3
+ Frontend on: 3000
4
+ Websocket: 3001
5
+ http-server: 3002
6
+
7
+ - This was made to fix my repeated workspace issues in configuring a turborepo with
8
+ - websockets
9
+ - http server
10
+ - prisma DB
11
+ - next FE
12
+
13
+ - To be added soon
14
+ - [ ] Tailwind setup
15
+ - [ ] custom Razorpay FE and BE setup.
16
+
17
+ This set up is functional and tested. this is to be used whenever one needs a mono repo with all these configured
18
+
19
+ Maintained by: Abhinav Patra
20
+
@@ -0,0 +1 @@
1
+ # `http-server`
@@ -0,0 +1,35 @@
1
+ import express from "express";
2
+ import cors from "cors"
3
+
4
+ import { client } from "db/client"
5
+
6
+ const app = express();
7
+ app.use(express.json());
8
+ app.use(cors());
9
+
10
+ app.get('/', (req, res) => {
11
+ return res.json({
12
+ "message":"/ endpoint"
13
+ })
14
+ })
15
+
16
+ app.post('/signup', async(req, res) => {
17
+ const username = req.body.username;
18
+ const password = req.body.password;
19
+ console.log(password);
20
+ const user = await client.user.create({
21
+ data: {
22
+ username: username,
23
+ password: password
24
+ }
25
+ })
26
+ console.log(user);
27
+ res.send({
28
+ "message": "hello user, you have signed up",
29
+ "user details": user
30
+ })
31
+ })
32
+
33
+ app.listen(3002, () => {
34
+ console.log("http server Listening on port 3002")
35
+ })
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "http-server",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "bun run index.ts",
7
+ "build": "echo 'Add build script here'",
8
+ "test": "echo 'Add test script here'",
9
+ "lint": "echo 'Add lint script here'"
10
+ },
11
+ "dependencies": {
12
+ "@types/cors": "^2.8.19",
13
+ "cors": "^2.8.6",
14
+ "express": "^5.2.1"
15
+ },
16
+ "devDependencies": {
17
+ "@repo/eslint-config": "*",
18
+ "@repo/typescript-config": "*",
19
+ "@types/express": "^5.0.6",
20
+ "db": "workspace:*"
21
+ }
22
+ }
@@ -0,0 +1 @@
1
+ # Frontend
@@ -0,0 +1 @@
1
+ @import "tailwindcss";
@@ -0,0 +1,31 @@
1
+ import type { Metadata } from "next";
2
+ import localFont from "next/font/local";
3
+ import "./globals.css";
4
+
5
+ const geistSans = localFont({
6
+ src: "./fonts/GeistVF.woff",
7
+ variable: "--font-geist-sans",
8
+ });
9
+ const geistMono = localFont({
10
+ src: "./fonts/GeistMonoVF.woff",
11
+ variable: "--font-geist-mono",
12
+ });
13
+
14
+ export const metadata: Metadata = {
15
+ title: "Create Next App",
16
+ description: "Generated by create next app",
17
+ };
18
+
19
+ export default function RootLayout({
20
+ children,
21
+ }: Readonly<{
22
+ children: React.ReactNode;
23
+ }>) {
24
+ return (
25
+ <html lang="en">
26
+ <body className={`${geistSans.variable} ${geistMono.variable}`}>
27
+ {children}
28
+ </body>
29
+ </html>
30
+ );
31
+ }
@@ -0,0 +1,186 @@
1
+ .page {
2
+ --gray-rgb: 0, 0, 0;
3
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.08);
4
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.05);
5
+
6
+ --button-primary-hover: #383838;
7
+ --button-secondary-hover: #f2f2f2;
8
+
9
+ display: grid;
10
+ grid-template-rows: 20px 1fr 20px;
11
+ align-items: center;
12
+ justify-items: center;
13
+ min-height: 100svh;
14
+ padding: 80px;
15
+ gap: 64px;
16
+ font-synthesis: none;
17
+ }
18
+
19
+ @media (prefers-color-scheme: dark) {
20
+ .page {
21
+ --gray-rgb: 255, 255, 255;
22
+ --gray-alpha-200: rgba(var(--gray-rgb), 0.145);
23
+ --gray-alpha-100: rgba(var(--gray-rgb), 0.06);
24
+
25
+ --button-primary-hover: #ccc;
26
+ --button-secondary-hover: #1a1a1a;
27
+ }
28
+ }
29
+
30
+ .main {
31
+ display: flex;
32
+ flex-direction: column;
33
+ gap: 32px;
34
+ grid-row-start: 2;
35
+ }
36
+
37
+ .main ol {
38
+ font-family: var(--font-geist-mono);
39
+ padding-left: 0;
40
+ margin: 0;
41
+ font-size: 14px;
42
+ line-height: 24px;
43
+ letter-spacing: -0.01em;
44
+ list-style-position: inside;
45
+ }
46
+
47
+ .main li:not(:last-of-type) {
48
+ margin-bottom: 8px;
49
+ }
50
+
51
+ .main code {
52
+ font-family: inherit;
53
+ background: var(--gray-alpha-100);
54
+ padding: 2px 4px;
55
+ border-radius: 4px;
56
+ font-weight: 600;
57
+ }
58
+
59
+ .ctas {
60
+ display: flex;
61
+ gap: 16px;
62
+ }
63
+
64
+ .ctas a {
65
+ appearance: none;
66
+ border-radius: 128px;
67
+ height: 48px;
68
+ padding: 0 20px;
69
+ font-family: var(--font-geist-sans);
70
+ border: 1px solid transparent;
71
+ transition: background 0.2s, color 0.2s, border-color 0.2s;
72
+ cursor: pointer;
73
+ display: flex;
74
+ align-items: center;
75
+ justify-content: center;
76
+ font-size: 16px;
77
+ line-height: 20px;
78
+ font-weight: 500;
79
+ }
80
+
81
+ a.primary {
82
+ background: var(--foreground);
83
+ color: var(--background);
84
+ gap: 8px;
85
+ }
86
+
87
+ a.secondary {
88
+ border-color: var(--gray-alpha-200);
89
+ min-width: 180px;
90
+ }
91
+
92
+ button.secondary {
93
+ appearance: none;
94
+ border-radius: 128px;
95
+ height: 48px;
96
+ padding: 0 20px;
97
+ font-family: var(--font-geist-sans);
98
+ border: 1px solid transparent;
99
+ transition: background 0.2s, color 0.2s, border-color 0.2s;
100
+ cursor: pointer;
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ font-size: 16px;
105
+ line-height: 20px;
106
+ font-weight: 500;
107
+ background: transparent;
108
+ border-color: var(--gray-alpha-200);
109
+ min-width: 180px;
110
+ }
111
+
112
+ .footer {
113
+ font-family: var(--font-geist-sans);
114
+ grid-row-start: 3;
115
+ display: flex;
116
+ gap: 24px;
117
+ }
118
+
119
+ .footer a {
120
+ display: flex;
121
+ align-items: center;
122
+ gap: 8px;
123
+ }
124
+
125
+ .footer img {
126
+ flex-shrink: 0;
127
+ }
128
+
129
+ /* Enable hover only on non-touch devices */
130
+ @media (hover: hover) and (pointer: fine) {
131
+ a.primary:hover {
132
+ background: var(--button-primary-hover);
133
+ border-color: transparent;
134
+ }
135
+
136
+ a.secondary:hover {
137
+ background: var(--button-secondary-hover);
138
+ border-color: transparent;
139
+ }
140
+
141
+ .footer a:hover {
142
+ text-decoration: underline;
143
+ text-underline-offset: 4px;
144
+ }
145
+ }
146
+
147
+ @media (max-width: 600px) {
148
+ .page {
149
+ padding: 32px;
150
+ padding-bottom: 80px;
151
+ }
152
+
153
+ .main {
154
+ align-items: center;
155
+ }
156
+
157
+ .main ol {
158
+ text-align: center;
159
+ }
160
+
161
+ .ctas {
162
+ flex-direction: column;
163
+ }
164
+
165
+ .ctas a {
166
+ font-size: 14px;
167
+ height: 40px;
168
+ padding: 0 16px;
169
+ }
170
+
171
+ a.secondary {
172
+ min-width: auto;
173
+ }
174
+
175
+ .footer {
176
+ flex-wrap: wrap;
177
+ align-items: center;
178
+ justify-content: center;
179
+ }
180
+ }
181
+
182
+ @media (prefers-color-scheme: dark) {
183
+ .logo {
184
+ filter: invert();
185
+ }
186
+ }
@@ -0,0 +1,25 @@
1
+ import Image, { type ImageProps } from "next/image";
2
+
3
+ type Props = Omit<ImageProps, "src"> & {
4
+ srcLight: string;
5
+ srcDark: string;
6
+ };
7
+
8
+ const ThemeImage = (props: Props) => {
9
+ const { srcLight, srcDark, ...rest } = props;
10
+
11
+ return (
12
+ <>
13
+ <Image {...rest} src={srcLight} className="imgLight" />
14
+ <Image {...rest} src={srcDark} className="imgDark" />
15
+ </>
16
+ );
17
+ };
18
+
19
+ export default function Home() {
20
+ return (
21
+ <div className="">
22
+ <h1>Hello World</h1>
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,4 @@
1
+ import { nextJsConfig } from "@repo/eslint-config/next-js";
2
+
3
+ /** @type {import("eslint").Linter.Config[]} */
4
+ export default nextJsConfig;
@@ -0,0 +1,4 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {};
3
+
4
+ export default nextConfig;
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "web",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "private": true,
6
+ "scripts": {
7
+ "dev": "next dev --port 3000",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "eslint --max-warnings 0",
11
+ "check-types": "next typegen && tsc --noEmit"
12
+ },
13
+ "dependencies": {
14
+ "@repo/ui": "*",
15
+ "next": "16.1.5",
16
+ "react": "^19.2.0",
17
+ "react-dom": "^19.2.0"
18
+ },
19
+ "devDependencies": {
20
+ "@repo/eslint-config": "*",
21
+ "@repo/typescript-config": "*",
22
+ "@types/node": "^22.15.3",
23
+ "@types/react": "19.2.2",
24
+ "@types/react-dom": "19.2.2",
25
+ "eslint": "^9.39.1",
26
+ "typescript": "5.9.2"
27
+ }
28
+ }
@@ -0,0 +1,7 @@
1
+ const config = {
2
+ plugins: {
3
+ "@tailwindcss/postcss": {},
4
+ },
5
+ };
6
+
7
+ export default config;
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "@repo/typescript-config/nextjs.json",
3
+ "compilerOptions": {
4
+ "plugins": [
5
+ {
6
+ "name": "next"
7
+ }
8
+ ]
9
+ },
10
+ "include": [
11
+ "**/*.ts",
12
+ "**/*.tsx",
13
+ "next-env.d.ts",
14
+ "next.config.js",
15
+ ".next/types/**/*.ts"
16
+ ],
17
+ "exclude": [
18
+ "node_modules"
19
+ ]
20
+ }
@@ -0,0 +1 @@
1
+ # `ws-server`
@@ -0,0 +1,25 @@
1
+ import { WebSocketServer } from "ws";
2
+ import { client } from "db/client"
3
+
4
+ const server = new WebSocketServer({
5
+ port: 3001
6
+ },() => {
7
+ console.log("WebSocket server is running on port 3001");
8
+ });
9
+
10
+ server.on("connection", async (socket) => {
11
+ const num = Math.random() * 100;
12
+ await client.user.create({
13
+ data: {
14
+ username: `Abhinav${num}`,
15
+ password: "12345678"
16
+ }
17
+ });
18
+
19
+ console.log("user connected");
20
+ socket.send("Connected to the websocket from turbo repo ws-server")
21
+ })
22
+
23
+ server.on("error", (error) => {
24
+ console.error("WebSocket server error:", error);
25
+ });
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "ws-server",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "bun run index.ts",
7
+ "build": "echo 'Add build script here'",
8
+ "test": "echo 'Add test script here'",
9
+ "lint": "echo 'Add lint script here'"
10
+ },
11
+ "dependencies": {
12
+ "@repo/eslint-config": "*",
13
+ "@repo/typescript-config": "*",
14
+ "@repo/ui": "*",
15
+ "@types/ws": "^8.18.1",
16
+ "db": "*",
17
+ "ws": "^8.19.0"
18
+ },
19
+ "module": "index.ts",
20
+ "type": "module",
21
+ "devDependencies": {
22
+ "@types/bun": "latest"
23
+ },
24
+ "peerDependencies": {
25
+ "typescript": "^5"
26
+ }
27
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }