create-turbo-mono 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 (54) hide show
  1. package/.claude/settings.local.json +14 -0
  2. package/README.md +182 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +118 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/scaffold.d.ts +8 -0
  8. package/dist/scaffold.d.ts.map +1 -0
  9. package/dist/scaffold.js +42 -0
  10. package/dist/scaffold.js.map +1 -0
  11. package/dist/templates/backend.d.ts +2 -0
  12. package/dist/templates/backend.d.ts.map +1 -0
  13. package/dist/templates/backend.js +424 -0
  14. package/dist/templates/backend.js.map +1 -0
  15. package/dist/templates/cicd.d.ts +3 -0
  16. package/dist/templates/cicd.d.ts.map +1 -0
  17. package/dist/templates/cicd.js +307 -0
  18. package/dist/templates/cicd.js.map +1 -0
  19. package/dist/templates/docker.d.ts +3 -0
  20. package/dist/templates/docker.d.ts.map +1 -0
  21. package/dist/templates/docker.js +458 -0
  22. package/dist/templates/docker.js.map +1 -0
  23. package/dist/templates/docs.d.ts +3 -0
  24. package/dist/templates/docs.d.ts.map +1 -0
  25. package/dist/templates/docs.js +71 -0
  26. package/dist/templates/docs.js.map +1 -0
  27. package/dist/templates/frontend.d.ts +2 -0
  28. package/dist/templates/frontend.d.ts.map +1 -0
  29. package/dist/templates/frontend.js +441 -0
  30. package/dist/templates/frontend.js.map +1 -0
  31. package/dist/templates/root.d.ts +3 -0
  32. package/dist/templates/root.d.ts.map +1 -0
  33. package/dist/templates/root.js +210 -0
  34. package/dist/templates/root.js.map +1 -0
  35. package/dist/templates/shared.d.ts +2 -0
  36. package/dist/templates/shared.d.ts.map +1 -0
  37. package/dist/templates/shared.js +696 -0
  38. package/dist/templates/shared.js.map +1 -0
  39. package/dist/utils.d.ts +5 -0
  40. package/dist/utils.d.ts.map +1 -0
  41. package/dist/utils.js +34 -0
  42. package/dist/utils.js.map +1 -0
  43. package/package.json +40 -0
  44. package/src/index.ts +138 -0
  45. package/src/scaffold.ts +51 -0
  46. package/src/templates/backend.ts +460 -0
  47. package/src/templates/cicd.ts +334 -0
  48. package/src/templates/docker.ts +503 -0
  49. package/src/templates/docs.ts +74 -0
  50. package/src/templates/frontend.ts +469 -0
  51. package/src/templates/root.ts +216 -0
  52. package/src/templates/shared.ts +820 -0
  53. package/src/utils.ts +31 -0
  54. package/tsconfig.json +20 -0
@@ -0,0 +1,503 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { ProjectConfig } from '../scaffold';
4
+
5
+ export async function createDockerFiles(config: ProjectConfig): Promise<void> {
6
+ const { targetDir, backends, frontends } = config;
7
+
8
+ await createDockerfileBase(targetDir, backends, frontends);
9
+ await createDockerComposeLocal(targetDir);
10
+ await createDockerComposeDev(targetDir, backends, frontends);
11
+ await createDockerComposeProd(targetDir, backends, frontends);
12
+ await createDockerIgnore(targetDir);
13
+ }
14
+
15
+ async function createDockerfileBase(
16
+ targetDir: string,
17
+ backends: string[],
18
+ frontends: string[]
19
+ ): Promise<void> {
20
+ const backendTargets = backends
21
+ .map(
22
+ (name) => `# ============================================
23
+ # ${name.toUpperCase()} Production
24
+ # ============================================
25
+ FROM build-base AS ${name}-production
26
+ WORKDIR /app/apps/backend/${name}
27
+
28
+ ENV NODE_OPTIONS="--max-old-space-size=4096"
29
+
30
+ RUN --mount=type=cache,id=nest,target=/app/node_modules/.cache \\
31
+ echo "📦 Building ${name}..." && \\
32
+ pnpm prisma generate && \\
33
+ pnpm run build
34
+
35
+ ENV NODE_OPTIONS="--max-old-space-size=2048"
36
+
37
+ EXPOSE 3000
38
+
39
+ CMD ["sh", "-c", "pnpm prisma generate && until pnpm prisma migrate deploy; do echo 'Waiting for DB...'; sleep 2; done && pnpm run start:prod"]
40
+
41
+ # ============================================
42
+ # ${name.toUpperCase()} Development
43
+ # ============================================
44
+ FROM shared-libs AS ${name}-development
45
+ WORKDIR /app/apps/backend/${name}
46
+ ENV NODE_ENV=development
47
+
48
+ EXPOSE 3000
49
+ CMD ["sh", "-c", "pnpm prisma generate && until pnpm prisma migrate deploy; do echo 'Waiting for DB...'; sleep 2; done && pnpm run dev"]`
50
+ )
51
+ .join('\n\n');
52
+
53
+ const frontendTargets = frontends
54
+ .map(
55
+ (name, index) => `# ============================================
56
+ # ${name.toUpperCase()} Production
57
+ # ============================================
58
+ FROM build-base AS ${name}-production
59
+ WORKDIR /app/apps/frontend/${name}
60
+
61
+ ENV NODE_OPTIONS="--max-old-space-size=4096"
62
+
63
+ RUN --mount=type=cache,id=next,target=/app/node_modules/.cache \\
64
+ echo "🌐 Building ${name}..." && \\
65
+ NEXT_TELEMETRY_DISABLED=1 pnpm next build
66
+
67
+ ENV NODE_OPTIONS="--max-old-space-size=2048"
68
+
69
+ EXPOSE ${3000 + index}
70
+ CMD ["pnpm", "run", "start"]`
71
+ )
72
+ .join('\n\n');
73
+
74
+ const dockerfile = `# syntax=docker/dockerfile:1
75
+
76
+ # ============================================
77
+ # Global Build Arguments
78
+ # ============================================
79
+ ARG NODE_VERSION=20
80
+ ARG DATABASE_URL
81
+
82
+ # ============================================
83
+ # Base Stage – Node + pnpm + Workspace setup
84
+ # ============================================
85
+ FROM node:\${NODE_VERSION}-alpine AS base
86
+
87
+ # Install pnpm
88
+ RUN corepack enable && corepack prepare pnpm@latest --activate
89
+
90
+ # Essentials
91
+ RUN apk add --no-cache curl bash postgresql-client
92
+
93
+ WORKDIR /app
94
+
95
+ # Workspace files
96
+ COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./
97
+ COPY turbo.json tsconfig.json ./
98
+ COPY packages/ packages/
99
+
100
+ # App-level package.json
101
+ ${backends.map((name) => `COPY apps/backend/${name}/package.json apps/backend/${name}/`).join('\n')}
102
+ ${frontends.map((name) => `COPY apps/frontend/${name}/package.json apps/frontend/${name}/`).join('\n')}
103
+
104
+ # ============================================
105
+ # Dependencies Stage
106
+ # ============================================
107
+ FROM base AS dependencies
108
+
109
+ RUN --mount=type=cache,id=pnpm,target=/root/.local/share/pnpm/store \\
110
+ pnpm install --frozen-lockfile
111
+
112
+ # ============================================
113
+ # Shared Libraries + Prisma Clients
114
+ # ============================================
115
+ FROM dependencies AS shared-libs
116
+
117
+ ARG DATABASE_URL
118
+ ENV DATABASE_URL=\${DATABASE_URL}
119
+
120
+ # Prisma schema
121
+ COPY packages/database/prisma/ packages/database/prisma/
122
+
123
+ # Generate Prisma client
124
+ RUN echo "🧩 Generating Prisma client..." && \\
125
+ cd packages/database && pnpm prisma generate
126
+
127
+ # Build shared packages
128
+ RUN --mount=type=cache,id=turbo,target=/app/node_modules/.cache \\
129
+ pnpm turbo run build --filter="@repo/shared-*"
130
+
131
+ # ============================================
132
+ # Common Build Base
133
+ # ============================================
134
+ FROM shared-libs AS build-base
135
+
136
+ # Source
137
+ COPY apps/ apps/
138
+
139
+ ENV NODE_ENV=production
140
+ ENV NEXT_TELEMETRY_DISABLED=1
141
+
142
+ ${backendTargets}
143
+
144
+ ${frontendTargets}
145
+ `;
146
+
147
+ await fs.writeFile(path.join(targetDir, 'Dockerfile'), dockerfile);
148
+ }
149
+
150
+ async function createDockerComposeLocal(targetDir: string): Promise<void> {
151
+ const dockerComposeLocal = `version: '3.8'
152
+
153
+ services:
154
+ # PostgreSQL Database
155
+ postgres:
156
+ image: postgres:15-alpine
157
+ container_name: \${PROJECT_NAME:-app}-postgres
158
+ restart: unless-stopped
159
+ environment:
160
+ POSTGRES_USER: \${DATABASE_USER:-postgres}
161
+ POSTGRES_PASSWORD: \${DATABASE_PASSWORD:-postgres}
162
+ POSTGRES_DB: \${DATABASE_NAME:-app_db}
163
+ ports:
164
+ - '\${DATABASE_PORT:-5432}:5432'
165
+ volumes:
166
+ - postgres_data:/var/lib/postgresql/data
167
+ healthcheck:
168
+ test: ['CMD-SHELL', 'pg_isready -U \${DATABASE_USER:-postgres}']
169
+ interval: 10s
170
+ timeout: 5s
171
+ retries: 5
172
+ networks:
173
+ - app-network
174
+
175
+ # Redis Cache
176
+ redis:
177
+ image: redis:7-alpine
178
+ container_name: \${PROJECT_NAME:-app}-redis
179
+ restart: unless-stopped
180
+ ports:
181
+ - '\${REDIS_PORT:-6379}:6379'
182
+ volumes:
183
+ - redis_data:/data
184
+ healthcheck:
185
+ test: ['CMD', 'redis-cli', 'ping']
186
+ interval: 10s
187
+ timeout: 5s
188
+ retries: 5
189
+ networks:
190
+ - app-network
191
+
192
+ networks:
193
+ app-network:
194
+ driver: bridge
195
+
196
+ volumes:
197
+ postgres_data:
198
+ name: \${PROJECT_NAME:-app}-postgres-data
199
+ redis_data:
200
+ name: \${PROJECT_NAME:-app}-redis-data
201
+ `;
202
+
203
+ await fs.writeFile(
204
+ path.join(targetDir, 'docker-compose.local.yml'),
205
+ dockerComposeLocal
206
+ );
207
+ }
208
+
209
+ async function createDockerComposeDev(
210
+ targetDir: string,
211
+ backends: string[],
212
+ frontends: string[]
213
+ ): Promise<void> {
214
+ const backendServices = backends
215
+ .map(
216
+ (name, index) => ` # ${name} Backend
217
+ ${name}:
218
+ build:
219
+ context: .
220
+ dockerfile: Dockerfile
221
+ target: ${name}-development
222
+ container_name: \${PROJECT_NAME:-app}-${name}
223
+ restart: unless-stopped
224
+ env_file: .env
225
+ environment:
226
+ NODE_ENV: development
227
+ DATABASE_URL: postgresql://\${DATABASE_USER:-postgres}:\${DATABASE_PASSWORD:-postgres}@postgres:5432/\${DATABASE_NAME:-app_db}
228
+ REDIS_URL: redis://redis:6379
229
+ volumes:
230
+ - ./apps/backend/${name}:/app/apps/backend/${name}
231
+ - ./packages:/app/packages
232
+ - /app/node_modules
233
+ - /app/apps/backend/${name}/node_modules
234
+ depends_on:
235
+ postgres:
236
+ condition: service_healthy
237
+ redis:
238
+ condition: service_healthy
239
+ ports:
240
+ - '\${${name.toUpperCase()}_PORT:-${4000 + index}}:3000'
241
+ networks:
242
+ - app-network`
243
+ )
244
+ .join('\n\n');
245
+
246
+ const frontendServices = frontends
247
+ .map(
248
+ (name, index) => ` # ${name} Frontend
249
+ ${name}:
250
+ build:
251
+ context: .
252
+ dockerfile: Dockerfile
253
+ target: ${name}-production
254
+ container_name: \${PROJECT_NAME:-app}-${name}
255
+ restart: unless-stopped
256
+ env_file: .env
257
+ environment:
258
+ NODE_ENV: development
259
+ NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL:-http://localhost:4000}
260
+ volumes:
261
+ - ./apps/frontend/${name}:/app/apps/frontend/${name}
262
+ - ./packages:/app/packages
263
+ - /app/node_modules
264
+ - /app/apps/frontend/${name}/node_modules
265
+ - /app/apps/frontend/${name}/.next
266
+ ports:
267
+ - '\${${name.toUpperCase()}_PORT:-${3000 + index}}:${3000 + index}'
268
+ networks:
269
+ - app-network`
270
+ )
271
+ .join('\n\n');
272
+
273
+ const dockerComposeDev = `version: '3.8'
274
+
275
+ services:
276
+ # PostgreSQL Database
277
+ postgres:
278
+ image: postgres:15-alpine
279
+ container_name: \${PROJECT_NAME:-app}-postgres-dev
280
+ restart: unless-stopped
281
+ environment:
282
+ POSTGRES_USER: \${DATABASE_USER:-postgres}
283
+ POSTGRES_PASSWORD: \${DATABASE_PASSWORD:-postgres}
284
+ POSTGRES_DB: \${DATABASE_NAME:-app_db}
285
+ ports:
286
+ - '\${DATABASE_PORT:-5432}:5432'
287
+ volumes:
288
+ - postgres_dev_data:/var/lib/postgresql/data
289
+ healthcheck:
290
+ test: ['CMD-SHELL', 'pg_isready -U \${DATABASE_USER:-postgres}']
291
+ interval: 10s
292
+ timeout: 5s
293
+ retries: 5
294
+ networks:
295
+ - app-network
296
+
297
+ # Redis Cache
298
+ redis:
299
+ image: redis:7-alpine
300
+ container_name: \${PROJECT_NAME:-app}-redis-dev
301
+ restart: unless-stopped
302
+ ports:
303
+ - '\${REDIS_PORT:-6379}:6379'
304
+ healthcheck:
305
+ test: ['CMD', 'redis-cli', 'ping']
306
+ interval: 10s
307
+ timeout: 5s
308
+ retries: 5
309
+ networks:
310
+ - app-network
311
+
312
+ ${backendServices}
313
+
314
+ ${frontendServices}
315
+
316
+ networks:
317
+ app-network:
318
+ driver: bridge
319
+
320
+ volumes:
321
+ postgres_dev_data:
322
+ name: \${PROJECT_NAME:-app}-postgres-dev-data
323
+ redis_dev_data:
324
+ name: \${PROJECT_NAME:-app}-redis-dev-data
325
+ `;
326
+
327
+ await fs.writeFile(
328
+ path.join(targetDir, 'docker-compose.dev.yml'),
329
+ dockerComposeDev
330
+ );
331
+ }
332
+
333
+ async function createDockerComposeProd(
334
+ targetDir: string,
335
+ backends: string[],
336
+ frontends: string[]
337
+ ): Promise<void> {
338
+ const backendServices = backends
339
+ .map(
340
+ (name, index) => ` # ${name} Backend
341
+ ${name}:
342
+ image: ghcr.io/\${GITHUB_REPOSITORY:-your-org/your-repo}/${name}:\${RELEASE_TAG:-latest}
343
+ container_name: \${PROJECT_NAME:-app}-${name}
344
+ restart: unless-stopped
345
+ env_file: .env
346
+ environment:
347
+ NODE_ENV: production
348
+ DATABASE_URL: postgresql://\${DATABASE_USER:-postgres}:\${DATABASE_PASSWORD:-postgres}@postgres:5432/\${DATABASE_NAME:-app_db}
349
+ REDIS_URL: redis://redis:6379
350
+ PORT: 3000
351
+ depends_on:
352
+ postgres:
353
+ condition: service_healthy
354
+ redis:
355
+ condition: service_healthy
356
+ ports:
357
+ - '\${${name.toUpperCase()}_PORT:-${4000 + index}}:3000'
358
+ healthcheck:
359
+ test: ['CMD-SHELL', 'wget -qO- http://127.0.0.1:3000/health || exit 1']
360
+ interval: 15s
361
+ timeout: 5s
362
+ retries: 10
363
+ start_period: 90s
364
+ networks:
365
+ - app-network`
366
+ )
367
+ .join('\n\n');
368
+
369
+ const frontendServices = frontends
370
+ .map(
371
+ (name, index) => ` # ${name} Frontend
372
+ ${name}:
373
+ image: ghcr.io/\${GITHUB_REPOSITORY:-your-org/your-repo}/${name}:\${RELEASE_TAG:-latest}
374
+ container_name: \${PROJECT_NAME:-app}-${name}
375
+ restart: unless-stopped
376
+ env_file: .env
377
+ environment:
378
+ NODE_ENV: production
379
+ HOST: 0.0.0.0
380
+ PORT: ${3000 + index}
381
+ NEXT_PUBLIC_API_URL: \${NEXT_PUBLIC_API_URL:-http://localhost:4000}
382
+ ports:
383
+ - '\${${name.toUpperCase()}_PORT:-${3000 + index}}:${3000 + index}'
384
+ healthcheck:
385
+ test: ['CMD-SHELL', 'wget -qO- http://127.0.0.1:${3000 + index}/ || exit 1']
386
+ interval: 20s
387
+ timeout: 5s
388
+ retries: 6
389
+ start_period: 60s
390
+ networks:
391
+ - app-network`
392
+ )
393
+ .join('\n\n');
394
+
395
+ const dockerComposeProd = `name: \${PROJECT_NAME:-app}-deploy
396
+
397
+ services:
398
+ # PostgreSQL Database
399
+ postgres:
400
+ image: postgres:15-alpine
401
+ restart: unless-stopped
402
+ env_file: .env
403
+ environment:
404
+ POSTGRES_USER: \${DATABASE_USER:-postgres}
405
+ POSTGRES_PASSWORD: \${DATABASE_PASSWORD:-postgres}
406
+ POSTGRES_DB: \${DATABASE_NAME:-app_db}
407
+ volumes:
408
+ - postgres_data:/var/lib/postgresql/data
409
+ healthcheck:
410
+ test: ['CMD-SHELL', 'pg_isready -U \${DATABASE_USER:-postgres} -d \${DATABASE_NAME:-app_db}']
411
+ interval: 10s
412
+ timeout: 5s
413
+ retries: 5
414
+ networks:
415
+ - app-network
416
+
417
+ # Redis Cache
418
+ redis:
419
+ image: redis:7-alpine
420
+ restart: unless-stopped
421
+ healthcheck:
422
+ test: ['CMD', 'redis-cli', 'ping']
423
+ interval: 10s
424
+ timeout: 5s
425
+ retries: 5
426
+ networks:
427
+ - app-network
428
+
429
+ ${backendServices}
430
+
431
+ ${frontendServices}
432
+
433
+ networks:
434
+ app-network:
435
+ driver: bridge
436
+
437
+ volumes:
438
+ postgres_data:
439
+ external: true
440
+ name: \${PROJECT_NAME:-app}-postgres-prod-data
441
+ redis_data:
442
+ name: \${PROJECT_NAME:-app}-redis-prod-data
443
+ `;
444
+
445
+ await fs.writeFile(
446
+ path.join(targetDir, 'docker-compose.prod.yml'),
447
+ dockerComposeProd
448
+ );
449
+ }
450
+
451
+ async function createDockerIgnore(targetDir: string): Promise<void> {
452
+ const dockerignore = `# Dependencies
453
+ node_modules
454
+ npm-debug.log*
455
+ yarn-debug.log*
456
+ yarn-error.log*
457
+ pnpm-debug.log*
458
+
459
+ # Build outputs
460
+ dist
461
+ .next
462
+ out
463
+ build
464
+ *.tsbuildinfo
465
+
466
+ # Testing
467
+ coverage
468
+ .nyc_output
469
+
470
+ # Environment
471
+ .env
472
+ .env.*
473
+ !.env.example
474
+
475
+ # Misc
476
+ .DS_Store
477
+ *.pem
478
+ .vscode
479
+ .idea
480
+
481
+ # Git
482
+ .git
483
+ .gitignore
484
+
485
+ # Docker
486
+ Dockerfile*
487
+ docker-compose*.yml
488
+ .dockerignore
489
+
490
+ # Documentation
491
+ *.md
492
+ !README.md
493
+
494
+ # CI/CD
495
+ .github
496
+ .gitlab-ci.yml
497
+
498
+ # Turborepo
499
+ .turbo
500
+ `;
501
+
502
+ await fs.writeFile(path.join(targetDir, '.dockerignore'), dockerignore);
503
+ }
@@ -0,0 +1,74 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { ProjectConfig } from '../scaffold';
4
+
5
+ export async function createDocumentation(config: ProjectConfig): Promise<void> {
6
+ const { targetDir, backends, frontends } = config;
7
+ const docsDir = path.join(targetDir, 'docs');
8
+
9
+ await fs.ensureDir(docsDir);
10
+
11
+ await createEnvGuide(docsDir, backends, frontends);
12
+ }
13
+
14
+ async function createEnvGuide(
15
+ docsDir: string,
16
+ backends: string[],
17
+ frontends: string[]
18
+ ): Promise<void> {
19
+ const content = `# Environment Variables
20
+
21
+ ## Required
22
+
23
+ \`\`\`bash
24
+ # Database
25
+ DATABASE_URL="postgresql://postgres:postgres@localhost:5432/app_db"
26
+
27
+ # Redis
28
+ REDIS_URL="redis://localhost:6379"
29
+ \`\`\`
30
+
31
+ ## Optional
32
+
33
+ \`\`\`bash
34
+ # JWT
35
+ JWT_SECRET="your-secret-key"
36
+ JWT_EXPIRES_IN="7d"
37
+
38
+ # CORS
39
+ CORS_ORIGINS="http://localhost:3000"
40
+
41
+ # Email (for CI/CD notifications)
42
+ SMTP_SERVER=smtp.gmail.com
43
+ SMTP_PORT=587
44
+ SMTP_USERNAME=your-email@gmail.com
45
+ SMTP_PASSWORD=your-app-password
46
+ \`\`\`
47
+
48
+ ## Docker
49
+
50
+ \`\`\`bash
51
+ NODE_VERSION=20
52
+ REGISTRY=ghcr.io
53
+ IMAGE_TAG=latest
54
+ \`\`\`
55
+
56
+ ## GitHub Actions Secrets
57
+
58
+ | Secret | Description |
59
+ |--------|-------------|
60
+ | \`SSH_HOST\` | Server IP/domain |
61
+ | \`SSH_USER\` | SSH username |
62
+ | \`SSH_KEY\` | Private SSH key |
63
+ | \`DEPLOY_PATH\` | Deployment directory |
64
+
65
+ ## Service Ports
66
+
67
+ | Service | Port |
68
+ |---------|------|
69
+ ${backends.map((name, i) => `| ${name} | ${4000 + i} |`).join('\n')}
70
+ ${frontends.map((name, i) => `| ${name} | ${3000 + i} |`).join('\n')}
71
+ `;
72
+
73
+ await fs.writeFile(path.join(docsDir, 'ENVIRONMENT.md'), content);
74
+ }