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,820 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+
4
+ export async function createSharedPackages(targetDir: string): Promise<void> {
5
+ await createTsConfigPackage(targetDir);
6
+ await createDatabasePackage(targetDir);
7
+ await createSharedBackendPackage(targetDir);
8
+ await createSharedFrontendPackage(targetDir);
9
+ await createSharedCommonPackage(targetDir);
10
+ }
11
+
12
+ async function createTsConfigPackage(targetDir: string): Promise<void> {
13
+ const packageDir = path.join(targetDir, 'packages', 'tsconfig');
14
+ await fs.ensureDir(packageDir);
15
+
16
+ // package.json
17
+ const packageJson = {
18
+ name: '@repo/tsconfig',
19
+ version: '1.0.0',
20
+ private: true,
21
+ };
22
+
23
+ await fs.writeJson(path.join(packageDir, 'package.json'), packageJson, {
24
+ spaces: 2,
25
+ });
26
+
27
+ // base.json
28
+ const baseConfig = {
29
+ compilerOptions: {
30
+ target: 'ES2020',
31
+ lib: ['ES2020'],
32
+ module: 'commonjs',
33
+ moduleResolution: 'node',
34
+ esModuleInterop: true,
35
+ skipLibCheck: true,
36
+ forceConsistentCasingInFileNames: true,
37
+ strict: true,
38
+ resolveJsonModule: true,
39
+ declaration: true,
40
+ declarationMap: true,
41
+ sourceMap: true,
42
+ incremental: true,
43
+ noUnusedLocals: true,
44
+ noUnusedParameters: true,
45
+ noImplicitReturns: true,
46
+ noFallthroughCasesInSwitch: true,
47
+ },
48
+ };
49
+
50
+ await fs.writeJson(path.join(packageDir, 'base.json'), baseConfig, {
51
+ spaces: 2,
52
+ });
53
+
54
+ // nestjs.json
55
+ const nestjsConfig = {
56
+ extends: './base.json',
57
+ compilerOptions: {
58
+ module: 'commonjs',
59
+ declaration: true,
60
+ removeComments: true,
61
+ emitDecoratorMetadata: true,
62
+ experimentalDecorators: true,
63
+ allowSyntheticDefaultImports: true,
64
+ target: 'ES2021',
65
+ sourceMap: true,
66
+ outDir: './dist',
67
+ baseUrl: './',
68
+ incremental: true,
69
+ skipLibCheck: true,
70
+ },
71
+ };
72
+
73
+ await fs.writeJson(path.join(packageDir, 'nestjs.json'), nestjsConfig, {
74
+ spaces: 2,
75
+ });
76
+
77
+ // nextjs.json
78
+ const nextjsConfig = {
79
+ extends: './base.json',
80
+ compilerOptions: {
81
+ target: 'ES2020',
82
+ lib: ['dom', 'dom.iterable', 'esnext'],
83
+ allowJs: true,
84
+ skipLibCheck: true,
85
+ strict: true,
86
+ forceConsistentCasingInFileNames: true,
87
+ noEmit: true,
88
+ esModuleInterop: true,
89
+ module: 'esnext',
90
+ moduleResolution: 'bundler',
91
+ resolveJsonModule: true,
92
+ isolatedModules: true,
93
+ jsx: 'preserve',
94
+ incremental: true,
95
+ plugins: [
96
+ {
97
+ name: 'next',
98
+ },
99
+ ],
100
+ },
101
+ };
102
+
103
+ await fs.writeJson(path.join(packageDir, 'nextjs.json'), nextjsConfig, {
104
+ spaces: 2,
105
+ });
106
+
107
+ // react-library.json
108
+ const reactLibConfig = {
109
+ extends: './base.json',
110
+ compilerOptions: {
111
+ target: 'ES2020',
112
+ lib: ['dom', 'dom.iterable', 'esnext'],
113
+ jsx: 'react-jsx',
114
+ module: 'esnext',
115
+ moduleResolution: 'bundler',
116
+ noEmit: false,
117
+ outDir: './dist',
118
+ },
119
+ };
120
+
121
+ await fs.writeJson(
122
+ path.join(packageDir, 'react-library.json'),
123
+ reactLibConfig,
124
+ { spaces: 2 }
125
+ );
126
+ }
127
+
128
+ async function createDatabasePackage(targetDir: string): Promise<void> {
129
+ const packageDir = path.join(targetDir, 'packages', 'database');
130
+ await fs.ensureDir(path.join(packageDir, 'prisma'));
131
+ await fs.ensureDir(path.join(packageDir, 'src'));
132
+
133
+ // package.json
134
+ const packageJson = {
135
+ name: '@repo/database',
136
+ version: '1.0.0',
137
+ private: true,
138
+ main: './src/index.ts',
139
+ types: './src/index.ts',
140
+ scripts: {
141
+ 'db:generate': 'prisma generate',
142
+ 'db:push': 'prisma db push',
143
+ 'db:migrate': 'prisma migrate dev',
144
+ 'db:studio': 'prisma studio',
145
+ build: 'tsc',
146
+ clean: 'rm -rf dist',
147
+ 'type-check': 'tsc --noEmit',
148
+ },
149
+ dependencies: {
150
+ '@prisma/client': '^5.7.1',
151
+ },
152
+ devDependencies: {
153
+ '@repo/tsconfig': 'workspace:*',
154
+ prisma: '^5.7.1',
155
+ typescript: '^5.3.3',
156
+ },
157
+ };
158
+
159
+ await fs.writeJson(path.join(packageDir, 'package.json'), packageJson, {
160
+ spaces: 2,
161
+ });
162
+
163
+ // tsconfig.json
164
+ const tsConfig = {
165
+ extends: '@repo/tsconfig/base.json',
166
+ compilerOptions: {
167
+ outDir: './dist',
168
+ rootDir: './src',
169
+ },
170
+ include: ['src/**/*'],
171
+ exclude: ['node_modules', 'dist'],
172
+ };
173
+
174
+ await fs.writeJson(path.join(packageDir, 'tsconfig.json'), tsConfig, {
175
+ spaces: 2,
176
+ });
177
+
178
+ // prisma/schema.prisma
179
+ const prismaSchema = `// This is your Prisma schema file,
180
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
181
+
182
+ generator client {
183
+ provider = "prisma-client-js"
184
+ output = "../node_modules/.prisma/client"
185
+ }
186
+
187
+ datasource db {
188
+ provider = "postgresql"
189
+ url = env("DATABASE_URL")
190
+ }
191
+
192
+ model User {
193
+ id String @id @default(cuid())
194
+ email String @unique
195
+ name String?
196
+ createdAt DateTime @default(now())
197
+ updatedAt DateTime @updatedAt
198
+
199
+ @@map("users")
200
+ }
201
+
202
+ model Post {
203
+ id String @id @default(cuid())
204
+ title String
205
+ content String?
206
+ published Boolean @default(false)
207
+ authorId String?
208
+ createdAt DateTime @default(now())
209
+ updatedAt DateTime @updatedAt
210
+
211
+ @@map("posts")
212
+ }
213
+ `;
214
+
215
+ await fs.writeFile(
216
+ path.join(packageDir, 'prisma', 'schema.prisma'),
217
+ prismaSchema
218
+ );
219
+
220
+ // src/index.ts
221
+ const indexTs = `import { PrismaClient } from '@prisma/client';
222
+
223
+ declare global {
224
+ // eslint-disable-next-line no-var
225
+ var prisma: PrismaClient | undefined;
226
+ }
227
+
228
+ export const prisma =
229
+ global.prisma ||
230
+ new PrismaClient({
231
+ log: process.env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
232
+ });
233
+
234
+ if (process.env.NODE_ENV !== 'production') {
235
+ global.prisma = prisma;
236
+ }
237
+
238
+ export * from '@prisma/client';
239
+ `;
240
+
241
+ await fs.writeFile(path.join(packageDir, 'src', 'index.ts'), indexTs);
242
+
243
+ // .env.example
244
+ const envExample = `DATABASE_URL="postgresql://user:password@localhost:5432/mydb?schema=public"
245
+ `;
246
+
247
+ await fs.writeFile(path.join(packageDir, '.env.example'), envExample);
248
+
249
+ // README.md
250
+ const readme = `# @repo/database
251
+
252
+ Shared Prisma database client and schema.
253
+
254
+ ## Usage
255
+
256
+ \`\`\`typescript
257
+ import { prisma } from '@repo/database';
258
+
259
+ // Use prisma client
260
+ const users = await prisma.user.findMany();
261
+ \`\`\`
262
+
263
+ ## Commands
264
+
265
+ \`\`\`bash
266
+ # Generate Prisma Client
267
+ pnpm db:generate
268
+
269
+ # Create and apply migrations
270
+ pnpm db:migrate
271
+
272
+ # Push schema changes without migration
273
+ pnpm db:push
274
+
275
+ # Open Prisma Studio
276
+ pnpm db:studio
277
+ \`\`\`
278
+ `;
279
+
280
+ await fs.writeFile(path.join(packageDir, 'README.md'), readme);
281
+ }
282
+
283
+ async function createSharedBackendPackage(targetDir: string): Promise<void> {
284
+ const packageDir = path.join(targetDir, 'packages', 'shared-backend');
285
+ await fs.ensureDir(path.join(packageDir, 'src'));
286
+ await fs.ensureDir(path.join(packageDir, 'src', 'utils'));
287
+ await fs.ensureDir(path.join(packageDir, 'src', 'middleware'));
288
+ await fs.ensureDir(path.join(packageDir, 'src', 'types'));
289
+
290
+ // package.json
291
+ const packageJson = {
292
+ name: '@repo/shared-backend',
293
+ version: '1.0.0',
294
+ private: true,
295
+ main: './src/index.ts',
296
+ types: './src/index.ts',
297
+ scripts: {
298
+ build: 'tsc',
299
+ clean: 'rm -rf dist',
300
+ 'type-check': 'tsc --noEmit',
301
+ },
302
+ dependencies: {
303
+ '@repo/shared-common': 'workspace:*',
304
+ },
305
+ devDependencies: {
306
+ '@repo/tsconfig': 'workspace:*',
307
+ '@types/node': '^20.10.6',
308
+ typescript: '^5.3.3',
309
+ },
310
+ };
311
+
312
+ await fs.writeJson(path.join(packageDir, 'package.json'), packageJson, {
313
+ spaces: 2,
314
+ });
315
+
316
+ // tsconfig.json
317
+ const tsConfig = {
318
+ extends: '@repo/tsconfig/base.json',
319
+ compilerOptions: {
320
+ outDir: './dist',
321
+ rootDir: './src',
322
+ },
323
+ include: ['src/**/*'],
324
+ exclude: ['node_modules', 'dist'],
325
+ };
326
+
327
+ await fs.writeJson(path.join(packageDir, 'tsconfig.json'), tsConfig, {
328
+ spaces: 2,
329
+ });
330
+
331
+ // src/index.ts
332
+ const indexTs = `export * from './utils';
333
+ export * from './types';
334
+ `;
335
+
336
+ await fs.writeFile(path.join(packageDir, 'src', 'index.ts'), indexTs);
337
+
338
+ // src/utils/index.ts
339
+ const utilsIndexTs = `export * from './logger';
340
+ export * from './response';
341
+ `;
342
+
343
+ await fs.writeFile(
344
+ path.join(packageDir, 'src', 'utils', 'index.ts'),
345
+ utilsIndexTs
346
+ );
347
+
348
+ // src/utils/logger.ts
349
+ const loggerTs = `export class Logger {
350
+ private context: string;
351
+
352
+ constructor(context: string) {
353
+ this.context = context;
354
+ }
355
+
356
+ log(message: string, ...args: any[]) {
357
+ console.log(\`[\${this.context}] \${message}\`, ...args);
358
+ }
359
+
360
+ error(message: string, ...args: any[]) {
361
+ console.error(\`[\${this.context}] ERROR: \${message}\`, ...args);
362
+ }
363
+
364
+ warn(message: string, ...args: any[]) {
365
+ console.warn(\`[\${this.context}] WARN: \${message}\`, ...args);
366
+ }
367
+
368
+ debug(message: string, ...args: any[]) {
369
+ if (process.env.NODE_ENV === 'development') {
370
+ console.debug(\`[\${this.context}] DEBUG: \${message}\`, ...args);
371
+ }
372
+ }
373
+ }
374
+ `;
375
+
376
+ await fs.writeFile(
377
+ path.join(packageDir, 'src', 'utils', 'logger.ts'),
378
+ loggerTs
379
+ );
380
+
381
+ // src/utils/response.ts
382
+ const responseTs = `export interface ApiResponse<T = any> {
383
+ success: boolean;
384
+ data?: T;
385
+ message?: string;
386
+ error?: string;
387
+ }
388
+
389
+ export function successResponse<T>(data: T, message?: string): ApiResponse<T> {
390
+ return {
391
+ success: true,
392
+ data,
393
+ message,
394
+ };
395
+ }
396
+
397
+ export function errorResponse(error: string, message?: string): ApiResponse {
398
+ return {
399
+ success: false,
400
+ error,
401
+ message,
402
+ };
403
+ }
404
+ `;
405
+
406
+ await fs.writeFile(
407
+ path.join(packageDir, 'src', 'utils', 'response.ts'),
408
+ responseTs
409
+ );
410
+
411
+ // src/types/index.ts
412
+ const typesIndexTs = `export interface PaginationParams {
413
+ page?: number;
414
+ limit?: number;
415
+ }
416
+
417
+ export interface PaginatedResponse<T> {
418
+ data: T[];
419
+ total: number;
420
+ page: number;
421
+ limit: number;
422
+ totalPages: number;
423
+ }
424
+ `;
425
+
426
+ await fs.writeFile(
427
+ path.join(packageDir, 'src', 'types', 'index.ts'),
428
+ typesIndexTs
429
+ );
430
+
431
+ // README.md
432
+ const readme = `# @repo/shared-backend
433
+
434
+ Shared backend utilities, types, and middleware.
435
+
436
+ ## Usage
437
+
438
+ \`\`\`typescript
439
+ import { Logger, successResponse } from '@repo/shared-backend';
440
+
441
+ const logger = new Logger('MyService');
442
+ logger.log('Hello from shared backend');
443
+
444
+ const response = successResponse({ id: 1 }, 'User created');
445
+ \`\`\`
446
+ `;
447
+
448
+ await fs.writeFile(path.join(packageDir, 'README.md'), readme);
449
+ }
450
+
451
+ async function createSharedFrontendPackage(targetDir: string): Promise<void> {
452
+ const packageDir = path.join(targetDir, 'packages', 'shared-frontend');
453
+ await fs.ensureDir(path.join(packageDir, 'src'));
454
+ await fs.ensureDir(path.join(packageDir, 'src', 'components'));
455
+ await fs.ensureDir(path.join(packageDir, 'src', 'hooks'));
456
+ await fs.ensureDir(path.join(packageDir, 'src', 'utils'));
457
+
458
+ // package.json
459
+ const packageJson = {
460
+ name: '@repo/shared-frontend',
461
+ version: '1.0.0',
462
+ private: true,
463
+ main: './src/index.ts',
464
+ types: './src/index.ts',
465
+ scripts: {
466
+ build: 'tsc',
467
+ clean: 'rm -rf dist',
468
+ 'type-check': 'tsc --noEmit',
469
+ lint: 'eslint "src/**/*.{ts,tsx}"',
470
+ },
471
+ dependencies: {
472
+ '@repo/shared-common': 'workspace:*',
473
+ react: '^18.2.0',
474
+ 'react-dom': '^18.2.0',
475
+ },
476
+ devDependencies: {
477
+ '@repo/tsconfig': 'workspace:*',
478
+ '@types/react': '^18.2.46',
479
+ '@types/react-dom': '^18.2.18',
480
+ typescript: '^5.3.3',
481
+ },
482
+ peerDependencies: {
483
+ react: '^18.2.0',
484
+ 'react-dom': '^18.2.0',
485
+ },
486
+ };
487
+
488
+ await fs.writeJson(path.join(packageDir, 'package.json'), packageJson, {
489
+ spaces: 2,
490
+ });
491
+
492
+ // tsconfig.json
493
+ const tsConfig = {
494
+ extends: '@repo/tsconfig/react-library.json',
495
+ compilerOptions: {
496
+ outDir: './dist',
497
+ rootDir: './src',
498
+ },
499
+ include: ['src/**/*'],
500
+ exclude: ['node_modules', 'dist'],
501
+ };
502
+
503
+ await fs.writeJson(path.join(packageDir, 'tsconfig.json'), tsConfig, {
504
+ spaces: 2,
505
+ });
506
+
507
+ // src/index.ts
508
+ const indexTs = `export * from './components';
509
+ export * from './hooks';
510
+ export * from './utils';
511
+ `;
512
+
513
+ await fs.writeFile(path.join(packageDir, 'src', 'index.ts'), indexTs);
514
+
515
+ // src/components/index.ts
516
+ const componentsIndexTs = `export * from './Button';
517
+ `;
518
+
519
+ await fs.writeFile(
520
+ path.join(packageDir, 'src', 'components', 'index.ts'),
521
+ componentsIndexTs
522
+ );
523
+
524
+ // src/components/Button.tsx
525
+ const buttonTsx = `import React from 'react';
526
+
527
+ export interface ButtonProps
528
+ extends React.ButtonHTMLAttributes<HTMLButtonElement> {
529
+ variant?: 'primary' | 'secondary' | 'danger';
530
+ size?: 'sm' | 'md' | 'lg';
531
+ }
532
+
533
+ export function Button({
534
+ children,
535
+ variant = 'primary',
536
+ size = 'md',
537
+ className = '',
538
+ ...props
539
+ }: ButtonProps) {
540
+ const baseStyles = 'font-semibold rounded focus:outline-none focus:ring-2';
541
+
542
+ const variantStyles = {
543
+ primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
544
+ secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',
545
+ danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
546
+ };
547
+
548
+ const sizeStyles = {
549
+ sm: 'px-3 py-1.5 text-sm',
550
+ md: 'px-4 py-2 text-base',
551
+ lg: 'px-6 py-3 text-lg',
552
+ };
553
+
554
+ const combinedClassName = \`\${baseStyles} \${variantStyles[variant]} \${sizeStyles[size]} \${className}\`;
555
+
556
+ return (
557
+ <button className={combinedClassName} {...props}>
558
+ {children}
559
+ </button>
560
+ );
561
+ }
562
+ `;
563
+
564
+ await fs.writeFile(
565
+ path.join(packageDir, 'src', 'components', 'Button.tsx'),
566
+ buttonTsx
567
+ );
568
+
569
+ // src/hooks/index.ts
570
+ const hooksIndexTs = `export * from './useLocalStorage';
571
+ `;
572
+
573
+ await fs.writeFile(
574
+ path.join(packageDir, 'src', 'hooks', 'index.ts'),
575
+ hooksIndexTs
576
+ );
577
+
578
+ // src/hooks/useLocalStorage.ts
579
+ const useLocalStorageTs = `import { useState, useEffect } from 'react';
580
+
581
+ export function useLocalStorage<T>(
582
+ key: string,
583
+ initialValue: T
584
+ ): [T, (value: T) => void] {
585
+ const [storedValue, setStoredValue] = useState<T>(() => {
586
+ if (typeof window === 'undefined') {
587
+ return initialValue;
588
+ }
589
+
590
+ try {
591
+ const item = window.localStorage.getItem(key);
592
+ return item ? JSON.parse(item) : initialValue;
593
+ } catch (error) {
594
+ console.error(error);
595
+ return initialValue;
596
+ }
597
+ });
598
+
599
+ const setValue = (value: T) => {
600
+ try {
601
+ setStoredValue(value);
602
+ if (typeof window !== 'undefined') {
603
+ window.localStorage.setItem(key, JSON.stringify(value));
604
+ }
605
+ } catch (error) {
606
+ console.error(error);
607
+ }
608
+ };
609
+
610
+ return [storedValue, setValue];
611
+ }
612
+ `;
613
+
614
+ await fs.writeFile(
615
+ path.join(packageDir, 'src', 'hooks', 'useLocalStorage.ts'),
616
+ useLocalStorageTs
617
+ );
618
+
619
+ // src/utils/index.ts
620
+ const utilsIndexTs = `export * from './cn';
621
+ `;
622
+
623
+ await fs.writeFile(
624
+ path.join(packageDir, 'src', 'utils', 'index.ts'),
625
+ utilsIndexTs
626
+ );
627
+
628
+ // src/utils/cn.ts
629
+ const cnTs = `export function cn(...classes: (string | undefined | null | false)[]): string {
630
+ return classes.filter(Boolean).join(' ');
631
+ }
632
+ `;
633
+
634
+ await fs.writeFile(path.join(packageDir, 'src', 'utils', 'cn.ts'), cnTs);
635
+
636
+ // README.md
637
+ const readme = `# @repo/shared-frontend
638
+
639
+ Shared frontend components, hooks, and utilities.
640
+
641
+ ## Usage
642
+
643
+ \`\`\`typescript
644
+ import { Button, useLocalStorage } from '@repo/shared-frontend';
645
+
646
+ function MyComponent() {
647
+ const [value, setValue] = useLocalStorage('myKey', 'default');
648
+
649
+ return <Button onClick={() => setValue('new value')}>Click me</Button>;
650
+ }
651
+ \`\`\`
652
+ `;
653
+
654
+ await fs.writeFile(path.join(packageDir, 'README.md'), readme);
655
+ }
656
+
657
+ async function createSharedCommonPackage(targetDir: string): Promise<void> {
658
+ const packageDir = path.join(targetDir, 'packages', 'shared-common');
659
+ await fs.ensureDir(path.join(packageDir, 'src'));
660
+ await fs.ensureDir(path.join(packageDir, 'src', 'utils'));
661
+ await fs.ensureDir(path.join(packageDir, 'src', 'constants'));
662
+ await fs.ensureDir(path.join(packageDir, 'src', 'types'));
663
+
664
+ // package.json
665
+ const packageJson = {
666
+ name: '@repo/shared-common',
667
+ version: '1.0.0',
668
+ private: true,
669
+ main: './src/index.ts',
670
+ types: './src/index.ts',
671
+ scripts: {
672
+ build: 'tsc',
673
+ clean: 'rm -rf dist',
674
+ 'type-check': 'tsc --noEmit',
675
+ },
676
+ devDependencies: {
677
+ '@repo/tsconfig': 'workspace:*',
678
+ typescript: '^5.3.3',
679
+ },
680
+ };
681
+
682
+ await fs.writeJson(path.join(packageDir, 'package.json'), packageJson, {
683
+ spaces: 2,
684
+ });
685
+
686
+ // tsconfig.json
687
+ const tsConfig = {
688
+ extends: '@repo/tsconfig/base.json',
689
+ compilerOptions: {
690
+ outDir: './dist',
691
+ rootDir: './src',
692
+ },
693
+ include: ['src/**/*'],
694
+ exclude: ['node_modules', 'dist'],
695
+ };
696
+
697
+ await fs.writeJson(path.join(packageDir, 'tsconfig.json'), tsConfig, {
698
+ spaces: 2,
699
+ });
700
+
701
+ // src/index.ts
702
+ const indexTs = `export * from './utils';
703
+ export * from './constants';
704
+ export * from './types';
705
+ `;
706
+
707
+ await fs.writeFile(path.join(packageDir, 'src', 'index.ts'), indexTs);
708
+
709
+ // src/utils/index.ts
710
+ const utilsIndexTs = `export * from './validation';
711
+ export * from './formatting';
712
+ `;
713
+
714
+ await fs.writeFile(
715
+ path.join(packageDir, 'src', 'utils', 'index.ts'),
716
+ utilsIndexTs
717
+ );
718
+
719
+ // src/utils/validation.ts
720
+ const validationTs = `export function isEmail(email: string): boolean {
721
+ const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
722
+ return emailRegex.test(email);
723
+ }
724
+
725
+ export function isValidUrl(url: string): boolean {
726
+ try {
727
+ new URL(url);
728
+ return true;
729
+ } catch {
730
+ return false;
731
+ }
732
+ }
733
+
734
+ export function isEmpty(value: any): boolean {
735
+ if (value == null) return true;
736
+ if (typeof value === 'string') return value.trim().length === 0;
737
+ if (Array.isArray(value)) return value.length === 0;
738
+ if (typeof value === 'object') return Object.keys(value).length === 0;
739
+ return false;
740
+ }
741
+ `;
742
+
743
+ await fs.writeFile(
744
+ path.join(packageDir, 'src', 'utils', 'validation.ts'),
745
+ validationTs
746
+ );
747
+
748
+ // src/utils/formatting.ts
749
+ const formattingTs = `export function formatDate(date: Date | string): string {
750
+ const d = typeof date === 'string' ? new Date(date) : date;
751
+ return d.toLocaleDateString('en-US', {
752
+ year: 'numeric',
753
+ month: 'long',
754
+ day: 'numeric',
755
+ });
756
+ }
757
+
758
+ export function capitalize(str: string): string {
759
+ if (!str) return '';
760
+ return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
761
+ }
762
+
763
+ export function truncate(str: string, maxLength: number): string {
764
+ if (str.length <= maxLength) return str;
765
+ return str.slice(0, maxLength - 3) + '...';
766
+ }
767
+ `;
768
+
769
+ await fs.writeFile(
770
+ path.join(packageDir, 'src', 'utils', 'formatting.ts'),
771
+ formattingTs
772
+ );
773
+
774
+ // src/constants/index.ts
775
+ const constantsIndexTs = `export const APP_NAME = 'My App';
776
+ export const API_VERSION = 'v1';
777
+ export const DEFAULT_PAGE_SIZE = 20;
778
+ export const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
779
+ `;
780
+
781
+ await fs.writeFile(
782
+ path.join(packageDir, 'src', 'constants', 'index.ts'),
783
+ constantsIndexTs
784
+ );
785
+
786
+ // src/types/index.ts
787
+ const typesIndexTs = `export interface BaseEntity {
788
+ id: string;
789
+ createdAt: Date;
790
+ updatedAt: Date;
791
+ }
792
+
793
+ export type Nullable<T> = T | null;
794
+
795
+ export type Optional<T> = T | undefined;
796
+ `;
797
+
798
+ await fs.writeFile(
799
+ path.join(packageDir, 'src', 'types', 'index.ts'),
800
+ typesIndexTs
801
+ );
802
+
803
+ // README.md
804
+ const readme = `# @repo/shared-common
805
+
806
+ Universal utilities and types shared across all packages.
807
+
808
+ ## Usage
809
+
810
+ \`\`\`typescript
811
+ import { isEmail, formatDate, APP_NAME } from '@repo/shared-common';
812
+
813
+ console.log(isEmail('test@example.com')); // true
814
+ console.log(formatDate(new Date())); // "January 1, 2024"
815
+ console.log(APP_NAME); // "My App"
816
+ \`\`\`
817
+ `;
818
+
819
+ await fs.writeFile(path.join(packageDir, 'README.md'), readme);
820
+ }