@newt-app/templates 0.0.2

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.
package/dist/index.js ADDED
@@ -0,0 +1,1781 @@
1
+ // src/index.ts
2
+ import { fileURLToPath } from "url";
3
+
4
+ // src/root/templates/package-json.ts
5
+ var package_json_default = {
6
+ filename: "package.json",
7
+ template: `{
8
+ "name": "<%= projectName %>",
9
+ "private": true,
10
+ "scripts": {
11
+ "build": "turbo run build",
12
+ "dev": "turbo run dev",
13
+ "lint": "turbo run lint",
14
+ "format": "prettier --write \\"**/*.{ts,tsx,js,jsx,json,md,yaml,yml}\\"",
15
+ "format:check": "prettier --check \\"**/*.{ts,tsx,js,jsx,json,md,yaml,yml}\\"",
16
+ "check-types": "turbo run check-types",
17
+ "db:migrate": "turbo run migrate",
18
+ "db:generate": "turbo run generate"
19
+ },
20
+ "devDependencies": {
21
+ "prettier": "^3.7.4",
22
+ "turbo": "^2.8.16",
23
+ "typescript": "5.9.2"
24
+ },
25
+ "packageManager": "pnpm@9.0.0",
26
+ "engines": {
27
+ "node": ">=18"
28
+ }
29
+ }`
30
+ };
31
+
32
+ // src/root/templates/gitignore.ts
33
+ var gitignore_default = {
34
+ filename: ".gitignore",
35
+ template: `# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
36
+
37
+ # Dependencies
38
+ node_modules
39
+ .pnp
40
+ .pnp.js
41
+
42
+ # Local env files
43
+ .env
44
+ .env.local
45
+ .env.development.local
46
+ .env.test.local
47
+ .env.production.local
48
+
49
+ # Testing
50
+ coverage
51
+
52
+ # Turbo
53
+ .turbo
54
+
55
+ # Vercel
56
+ .vercel
57
+
58
+ # Build Outputs
59
+ .next/
60
+ out/
61
+ build
62
+ dist
63
+
64
+
65
+ # Debug
66
+ npm-debug.log*
67
+ yarn-debug.log*
68
+ yarn-error.log*
69
+
70
+ # Misc
71
+ .DS_Store
72
+ *.pem`
73
+ };
74
+
75
+ // src/root/templates/prettierrc.ts
76
+ var prettierrc_default = {
77
+ filename: ".prettierrc",
78
+ template: `{
79
+ "singleQuote": true,
80
+ "trailingComma": "all"
81
+ }`
82
+ };
83
+
84
+ // src/root/templates/dockerignore.ts
85
+ var dockerignore_default = {
86
+ filename: ".dockerignore",
87
+ template: `node_modules
88
+ .next
89
+ dist
90
+ .turbo
91
+ *.log
92
+ .env
93
+ .env.*
94
+ !.env.example`
95
+ };
96
+
97
+ // src/root/templates/npmrc.ts
98
+ var npmrc_default = {
99
+ filename: ".npmrc",
100
+ template: ``
101
+ };
102
+
103
+ // src/root/templates/turbo-json.ts
104
+ var turbo_json_default = {
105
+ filename: "turbo.json",
106
+ template: `{
107
+ "$schema": "https://turborepo.dev/schema.json",
108
+ "ui": "tui",
109
+ "tasks": {
110
+ "build": {
111
+ "dependsOn": ["^build"],
112
+ "inputs": ["$TURBO_DEFAULT$", ".env*"],
113
+ "outputs": [".next/**", "!.next/cache/**"]
114
+ },
115
+ "lint": {
116
+ "dependsOn": ["^lint"]
117
+ },
118
+ "check-types": {
119
+ "dependsOn": ["^check-types"]
120
+ },
121
+ "dev": {
122
+ "cache": false,
123
+ "persistent": true
124
+ },
125
+ "migrate": {
126
+ "cache": false,
127
+ "interactive": true
128
+ },
129
+ "generate": {
130
+ "cache": false
131
+ }
132
+ }
133
+ }`
134
+ };
135
+
136
+ // src/root/templates/pnpm-workspace.ts
137
+ var pnpm_workspace_default = {
138
+ filename: "pnpm-workspace.yaml",
139
+ template: `packages:
140
+ - "apps/*"
141
+ - "packages/*"`
142
+ };
143
+
144
+ // src/root/templates/readme.ts
145
+ var readme_default = {
146
+ filename: "README.md",
147
+ template: `# <%= projectName %>
148
+
149
+ Full-stack monorepo: Next.js 16 + NestJS 11 + better-auth + PostgreSQL.
150
+
151
+ ## Quick start
152
+
153
+ \`\`\`sh
154
+ cp .env.example .env # fill in DATABASE_URL
155
+ pnpm install
156
+ pnpm db:migrate
157
+ pnpm dev
158
+ \`\`\`
159
+
160
+ Open [http://localhost:3000](http://localhost:3000).
161
+
162
+ ## Apps
163
+
164
+ - **web** \u2014 Next.js frontend (port 3000)
165
+ - **api** \u2014 NestJS backend (port 3001)
166
+
167
+ ## Packages
168
+
169
+ - **\`@repo/auth\`** \u2014 better-auth config
170
+ - **\`@repo/ui\`** \u2014 shared React components
171
+ - **\`@repo/eslint-config\`** \u2014 shared ESLint config
172
+ - **\`@repo/typescript-config\`** \u2014 shared tsconfig
173
+ `
174
+ };
175
+
176
+ // src/root/templates/env-example.ts
177
+ var env_example_default = {
178
+ filename: ".env.example",
179
+ template: `DATABASE_URL=postgresql://user:password@localhost:5432/dbname
180
+ BETTER_AUTH_URL=http://localhost:3000
181
+ BETTER_AUTH_SECRET=your-secret-here`
182
+ };
183
+
184
+ // src/root/index.ts
185
+ var root = {
186
+ templates: [
187
+ package_json_default,
188
+ gitignore_default,
189
+ prettierrc_default,
190
+ dockerignore_default,
191
+ npmrc_default,
192
+ turbo_json_default,
193
+ pnpm_workspace_default,
194
+ readme_default,
195
+ env_example_default
196
+ ]
197
+ };
198
+ var root_default = root;
199
+
200
+ // src/web/templates/package-json.ts
201
+ var package_json_default2 = {
202
+ filename: "apps/web/package.json",
203
+ template: `{
204
+ "name": "web",
205
+ "version": "0.1.0",
206
+ "type": "module",
207
+ "private": true,
208
+ "scripts": {
209
+ "dev": "next dev --port 3000",
210
+ "build": "next build",
211
+ "start": "next start",
212
+ "lint": "eslint --max-warnings 0",
213
+ "check-types": "next typegen && tsc --noEmit",
214
+ "db:migrate": "better-auth migrate"
215
+ },
216
+ "dependencies": {
217
+ "@repo/auth": "workspace:*",
218
+ "@repo/ui": "workspace:*",
219
+ "@tailwindcss/postcss": "^4.2.1",
220
+ "@tanstack/react-form": "^1.28.5",
221
+ "@tanstack/react-query": "^5.90.21",
222
+ "better-auth": "^1.2.8",
223
+ "next": "16.1.5",
224
+ "react": "^19.2.0",
225
+ "react-dom": "^19.2.0",
226
+ "tailwindcss": "^4.2.1"
227
+ },
228
+ "devDependencies": {
229
+ "@repo/eslint-config": "workspace:*",
230
+ "@repo/typescript-config": "workspace:*",
231
+ "@types/node": "^22.15.3",
232
+ "@types/react": "19.2.2",
233
+ "@types/react-dom": "19.2.2",
234
+ "eslint": "^9.39.1",
235
+ "typescript": "5.9.2"
236
+ }
237
+ }`
238
+ };
239
+
240
+ // src/web/templates/gitignore.ts
241
+ var gitignore_default2 = {
242
+ filename: "apps/web/.gitignore",
243
+ template: `# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
244
+
245
+ # dependencies
246
+ /node_modules
247
+ /.pnp
248
+ .pnp.js
249
+ .yarn/install-state.gz
250
+
251
+ # testing
252
+ /coverage
253
+
254
+ # next.js
255
+ /.next/
256
+ /out/
257
+
258
+ # production
259
+ /build
260
+
261
+ # misc
262
+ .DS_Store
263
+ *.pem
264
+
265
+ # debug
266
+ npm-debug.log*
267
+ yarn-debug.log*
268
+ yarn-error.log*
269
+
270
+ # env files (can opt-in for commiting if needed)
271
+ .env*
272
+
273
+ # vercel
274
+ .vercel
275
+
276
+ # typescript
277
+ *.tsbuildinfo
278
+ next-env.d.ts`
279
+ };
280
+
281
+ // src/web/templates/readme.ts
282
+ var readme_default2 = {
283
+ filename: "apps/web/README.md",
284
+ template: `This is a [Next.js](https://nextjs.org) project bootstrapped with [\`create-next-app\`](https://nextjs.org/docs/app/api-reference/create-next-app).
285
+
286
+ ## Getting Started
287
+
288
+ First, run the development server:
289
+
290
+ \`\`\`bash
291
+ npm run dev
292
+ # or
293
+ yarn dev
294
+ # or
295
+ pnpm dev
296
+ # or
297
+ bun dev
298
+ \`\`\`
299
+
300
+ Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
301
+
302
+ You can start editing the page by modifying \`app/page.tsx\`. The page auto-updates as you edit the file.
303
+
304
+ This project uses [\`next/font\`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load Inter, a custom Google Font.
305
+
306
+ ## Learn More
307
+
308
+ To learn more about Next.js, take a look at the following resources:
309
+
310
+ - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
311
+ - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
312
+
313
+ You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
314
+
315
+ ## Deploy on Vercel
316
+
317
+ The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
318
+
319
+ Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.`
320
+ };
321
+
322
+ // src/web/templates/eslint-config.ts
323
+ var eslint_config_default = {
324
+ filename: "apps/web/eslint.config.js",
325
+ template: `import { nextJsConfig } from "@repo/eslint-config/next-js";
326
+
327
+ /** @type {import("eslint").Linter.Config[]} */
328
+ export default nextJsConfig;`
329
+ };
330
+
331
+ // src/web/templates/next-config.ts
332
+ var next_config_default = {
333
+ filename: "apps/web/next.config.js",
334
+ template: `/** @type {import('next').NextConfig} */
335
+ const nextConfig = {
336
+ output: "standalone",
337
+ async rewrites() {
338
+ return [
339
+ {
340
+ source: '/api/:path*',
341
+ destination: 'http://localhost:3001/api/:path*',
342
+ },
343
+ ];
344
+ },
345
+ };
346
+
347
+ export default nextConfig;`
348
+ };
349
+
350
+ // src/web/templates/postcss-config.ts
351
+ var postcss_config_default = {
352
+ filename: "apps/web/postcss.config.mjs",
353
+ template: `const config = {
354
+ plugins: {
355
+ "@tailwindcss/postcss": {},
356
+ },
357
+ };
358
+
359
+ export default config;`
360
+ };
361
+
362
+ // src/web/templates/tsconfig.ts
363
+ var tsconfig_default = {
364
+ filename: "apps/web/tsconfig.json",
365
+ template: `{
366
+ "extends": "@repo/typescript-config/nextjs.json",
367
+ "compilerOptions": {
368
+ "plugins": [
369
+ {
370
+ "name": "next"
371
+ }
372
+ ],
373
+ "strictNullChecks": true
374
+ },
375
+ "include": [
376
+ "**/*.ts",
377
+ "**/*.tsx",
378
+ "next-env.d.ts",
379
+ "next.config.js",
380
+ ".next/types/**/*.ts"
381
+ ],
382
+ "exclude": ["node_modules"]
383
+ }`
384
+ };
385
+
386
+ // src/web/templates/globals-css.ts
387
+ var globals_css_default = {
388
+ filename: "apps/web/app/globals.css",
389
+ template: `@import 'tailwindcss';
390
+
391
+ html,
392
+ body {
393
+ height: 100%;
394
+ }
395
+
396
+ body {
397
+ color: #374151;
398
+ background-color: #ffffff;
399
+ -webkit-font-smoothing: antialiased;
400
+ }
401
+
402
+ * {
403
+ @apply border-gray-200;
404
+ }`
405
+ };
406
+
407
+ // src/web/templates/layout.ts
408
+ var layout_default = {
409
+ filename: "apps/web/app/layout.tsx",
410
+ template: `import type { Metadata } from "next";
411
+ import localFont from "next/font/local";
412
+ import Providers from "./providers";
413
+ import "./globals.css";
414
+
415
+ const geistSans = localFont({
416
+ src: "./fonts/GeistVF.woff",
417
+ variable: "--font-geist-sans",
418
+ });
419
+ const geistMono = localFont({
420
+ src: "./fonts/GeistMonoVF.woff",
421
+ variable: "--font-geist-mono",
422
+ });
423
+
424
+ export const metadata: Metadata = {
425
+ title: "<%= projectName %>",
426
+ description: "Next + Nest = Newt",
427
+ };
428
+
429
+ export default function RootLayout({
430
+ children,
431
+ }: Readonly<{
432
+ children: React.ReactNode;
433
+ }>) {
434
+ return (
435
+ <html lang="en">
436
+ <body className={\`\${geistSans.variable} \${geistMono.variable}\`}>
437
+ <Providers>{children}</Providers>
438
+ </body>
439
+ </html>
440
+ );
441
+ }`
442
+ };
443
+
444
+ // src/web/templates/page.ts
445
+ var page_default = {
446
+ filename: "apps/web/app/page.tsx",
447
+ template: `'use client';
448
+
449
+ import { useQuery } from '@tanstack/react-query';
450
+ import { authClient } from '../lib/auth-client';
451
+ import { AuthForm } from './auth-form';
452
+ import { Link } from '@repo/ui/link';
453
+ import { TodoList } from './todo-list';
454
+
455
+ export default function Home() {
456
+ const { data: session, isPending } = authClient.useSession();
457
+
458
+ const { data: hello } = useQuery({
459
+ queryKey: ['hello'],
460
+ queryFn: () => fetch('/api/hello').then((r) => r.json()),
461
+ });
462
+
463
+ return (
464
+ <main className="max-w-lg mx-auto border-r border-l h-full">
465
+ <div className="border-b p-4">
466
+ <p className="font-mono">apps/web/page.tsx</p>
467
+ <p className="text-gray-500">Delete me to get started!</p>
468
+ </div>
469
+ <div className="border-b p-4">
470
+ <h1 className="text-5xl font-black tracking-tight bg-linear-to-r from-indigo-500 via-purple-500 to-pink-500 bg-clip-text text-transparent">
471
+ <%= projectName %>
472
+ </h1>
473
+ <p className="mt-2 text-sm text-gray-500 tracking-widest uppercase">
474
+ Next + Nest = Newt \u{1F49C}
475
+ </p>
476
+ <p className="text-gray-500">The perfect TypeScript monorepo setup</p>
477
+ </div>
478
+ <div className="border-b p-4 text-gray-500">
479
+ <div className="flex justify-end text-gray-500">
480
+ <h2 className="uppercase">Next.js</h2>
481
+ </div>
482
+ <p className="font-mono">apps/web/layout.tsx</p>
483
+ <p>Next.js rendering</p>
484
+ </div>
485
+
486
+ <div className="p-4 border-b">
487
+ <div className="flex justify-end text-gray-500">
488
+ <h2 className="uppercase">Nest</h2>
489
+ </div>
490
+ <span className="font-mono text-gray-500 text-xs">
491
+ HTTP GET /api/hello
492
+ </span>
493
+ <pre className="border p-2 rounded bg-gray-50">
494
+ <code>{JSON.stringify(hello, null, 2)}</code>
495
+ </pre>
496
+ </div>
497
+ <div className="p-4 border-b">
498
+ <div className="flex justify-end text-gray-500">
499
+ <h2>better-auth</h2>
500
+ </div>
501
+ {isPending ? (
502
+ <p className="text-sm text-gray-500">Loading\u2026</p>
503
+ ) : session ? (
504
+ <TodoList session={session} />
505
+ ) : (
506
+ <AuthForm />
507
+ )}
508
+ </div>
509
+ <div className="p-4 text-sm">
510
+ <p className="mb-2">Learn more</p>
511
+ <ul className="list-disc list-inside space-y-2 mt-2">
512
+ <li>
513
+ <Link href="https://github.com">GitHub</Link>
514
+ </li>
515
+ <li>
516
+ <Link href="https://nextjs.org">Next.js</Link>
517
+ </li>
518
+ <li>
519
+ <Link href="https://nestjs.com">NestJS</Link>
520
+ </li>
521
+ </ul>
522
+ </div>
523
+ </main>
524
+ );
525
+ }`
526
+ };
527
+
528
+ // src/web/templates/providers.ts
529
+ var providers_default = {
530
+ filename: "apps/web/app/providers.tsx",
531
+ template: `'use client';
532
+
533
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
534
+ import { useState } from 'react';
535
+
536
+ export default function Providers({ children }: { children: React.ReactNode }) {
537
+ const [client] = useState(() => new QueryClient());
538
+ return <QueryClientProvider client={client}>{children}</QueryClientProvider>;
539
+ }`
540
+ };
541
+
542
+ // src/web/templates/auth-form.ts
543
+ var auth_form_default = {
544
+ filename: "apps/web/app/auth-form.tsx",
545
+ template: `'use client';
546
+
547
+ import { useForm } from '@tanstack/react-form';
548
+ import { useState } from 'react';
549
+ import { authClient } from '../lib/auth-client';
550
+
551
+ export function AuthForm() {
552
+ const [tab, setTab] = useState<'signin' | 'signup'>('signin');
553
+ const [error, setError] = useState('');
554
+
555
+ const form = useForm({
556
+ defaultValues: { name: '', email: '', password: '' },
557
+ onSubmit: async ({ value }) => {
558
+ setError('');
559
+ if (tab === 'signin') {
560
+ const { error } = await authClient.signIn.email(value);
561
+ if (error) setError(error.message ?? 'Sign in failed');
562
+ } else {
563
+ const { error } = await authClient.signUp.email(value);
564
+ if (error) setError(error.message ?? 'Sign up failed');
565
+ }
566
+ },
567
+ });
568
+
569
+ return (
570
+ <>
571
+ <div className="flex gap-2 mb-6 text-sm">
572
+ <button
573
+ onClick={() => setTab('signin')}
574
+ className={
575
+ tab === 'signin'
576
+ ? 'font-semibold'
577
+ : 'text-gray-500 hover:text-gray-700'
578
+ }
579
+ >
580
+ Sign in
581
+ </button>
582
+ <span className="text-gray-200">|</span>
583
+ <button
584
+ onClick={() => setTab('signup')}
585
+ className={
586
+ tab === 'signup'
587
+ ? 'font-semibold'
588
+ : 'text-gray-500 hover:text-gray-700'
589
+ }
590
+ >
591
+ Sign up
592
+ </button>
593
+ </div>
594
+
595
+ <form
596
+ onSubmit={(e) => {
597
+ e.preventDefault();
598
+ form.handleSubmit();
599
+ }}
600
+ className="space-y-4"
601
+ >
602
+ {tab === 'signup' && (
603
+ <form.Field name="name">
604
+ {(field) => (
605
+ <div className="space-y-1">
606
+ <label className="text-sm font-medium">Name</label>
607
+ <input
608
+ type="text"
609
+ value={field.state.value}
610
+ onChange={(e) => field.handleChange(e.target.value)}
611
+ required
612
+ className="w-full border border-gray-200 rounded px-3 py-2 text-sm"
613
+ />
614
+ </div>
615
+ )}
616
+ </form.Field>
617
+ )}
618
+
619
+ <form.Field name="email">
620
+ {(field) => (
621
+ <div className="space-y-1">
622
+ <label className="text-sm font-medium">Email</label>
623
+ <input
624
+ type="email"
625
+ value={field.state.value}
626
+ onChange={(e) => field.handleChange(e.target.value)}
627
+ required
628
+ className="w-full border border-gray-200 rounded px-3 py-2 text-sm"
629
+ />
630
+ </div>
631
+ )}
632
+ </form.Field>
633
+
634
+ <form.Field name="password">
635
+ {(field) => (
636
+ <div className="space-y-1">
637
+ <label className="text-sm font-medium">Password</label>
638
+ <input
639
+ type="password"
640
+ value={field.state.value}
641
+ onChange={(e) => field.handleChange(e.target.value)}
642
+ required
643
+ minLength={8}
644
+ className="w-full border border-gray-200 rounded px-3 py-2 text-sm"
645
+ />
646
+ </div>
647
+ )}
648
+ </form.Field>
649
+
650
+ {error && <p className="text-sm text-red-500">{error}</p>}
651
+
652
+ <form.Subscribe selector={(s) => s.isSubmitting}>
653
+ {(isSubmitting) => (
654
+ <button
655
+ type="submit"
656
+ disabled={isSubmitting}
657
+ className="w-full bg-gray-900 text-white rounded py-2 text-sm font-medium hover:bg-gray-700 disabled:opacity-40"
658
+ >
659
+ {isSubmitting
660
+ ? 'Loading\u2026'
661
+ : tab === 'signin'
662
+ ? 'Sign in'
663
+ : 'Create account'}
664
+ </button>
665
+ )}
666
+ </form.Subscribe>
667
+ </form>
668
+ </>
669
+ );
670
+ }`
671
+ };
672
+
673
+ // src/web/templates/todo-list.ts
674
+ var todo_list_default = {
675
+ filename: "apps/web/app/todo-list.tsx",
676
+ template: `'use client';
677
+
678
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
679
+ import { useForm } from '@tanstack/react-form';
680
+ import { authClient } from '../lib/auth-client';
681
+
682
+ interface Todo {
683
+ id: number;
684
+ title: string;
685
+ done: boolean;
686
+ }
687
+
688
+ const api = {
689
+ getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
690
+ createTodo: (title: string): Promise<Todo> =>
691
+ fetch('/api/todos', {
692
+ method: 'POST',
693
+ headers: { 'Content-Type': 'application/json' },
694
+ body: JSON.stringify({ title }),
695
+ }).then((r) => r.json()),
696
+ toggleTodo: (id: number): Promise<Todo> =>
697
+ fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
698
+ deleteTodo: (id: number): Promise<void> =>
699
+ fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
700
+ };
701
+
702
+ export function TodoList({
703
+ session,
704
+ }: {
705
+ session: { user: { email: string } };
706
+ }) {
707
+ const queryClient = useQueryClient();
708
+
709
+ const { data: todos = [], isPending } = useQuery({
710
+ queryKey: ['todos'],
711
+ queryFn: api.getTodos,
712
+ });
713
+
714
+ const createMutation = useMutation({
715
+ mutationFn: api.createTodo,
716
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
717
+ });
718
+
719
+ const toggleMutation = useMutation({
720
+ mutationFn: api.toggleTodo,
721
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
722
+ });
723
+
724
+ const deleteMutation = useMutation({
725
+ mutationFn: api.deleteTodo,
726
+ onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
727
+ });
728
+
729
+ const form = useForm({
730
+ defaultValues: { title: '' },
731
+ onSubmit: async ({ value }) => {
732
+ if (!value.title.trim()) return;
733
+ createMutation.mutate(value.title.trim());
734
+ form.reset();
735
+ },
736
+ });
737
+
738
+ return (
739
+ <>
740
+ <div className="flex items-center justify-between mb-8">
741
+ <h1 className="text-2xl font-semibold">Todos</h1>
742
+ <div className="flex items-center gap-3 text-sm text-gray-500">
743
+ <span>{session.user.email}</span>
744
+ <button
745
+ className="hover:text-gray-900"
746
+ onClick={() => authClient.signOut()}
747
+ >
748
+ Sign out
749
+ </button>
750
+ </div>
751
+ </div>
752
+
753
+ <form
754
+ onSubmit={(e) => {
755
+ e.preventDefault();
756
+ form.handleSubmit();
757
+ }}
758
+ className="flex gap-2 mb-6"
759
+ >
760
+ <form.Field name="title">
761
+ {(field) => (
762
+ <input
763
+ value={field.state.value}
764
+ onChange={(e) => field.handleChange(e.target.value)}
765
+ placeholder="New todo\u2026"
766
+ className="flex-1 border border-gray-200 rounded px-3 py-2 text-sm"
767
+ />
768
+ )}
769
+ </form.Field>
770
+
771
+ <form.Subscribe selector={(s) => s.isSubmitting}>
772
+ {(isSubmitting) => (
773
+ <button
774
+ type="submit"
775
+ disabled={isSubmitting || createMutation.isPending}
776
+ className="bg-gray-900 text-white rounded px-4 py-2 text-sm hover:bg-gray-700 disabled:opacity-40"
777
+ >
778
+ Add
779
+ </button>
780
+ )}
781
+ </form.Subscribe>
782
+ </form>
783
+
784
+ {isPending ? (
785
+ <p className="text-sm text-gray-500">Loading\u2026</p>
786
+ ) : (
787
+ <ul className="divide-y divide-gray-100">
788
+ {todos.map((todo) => (
789
+ <li key={todo.id} className="flex items-center gap-3 py-3">
790
+ <input
791
+ type="checkbox"
792
+ checked={todo.done}
793
+ onChange={() => toggleMutation.mutate(todo.id)}
794
+ />
795
+ <span
796
+ className={\`flex-1 text-sm \${todo.done ? 'line-through text-gray-500' : ''}\`}
797
+ >
798
+ {todo.title}
799
+ </span>
800
+ <button
801
+ onClick={() => deleteMutation.mutate(todo.id)}
802
+ className="text-gray-300 hover:text-red-400 text-lg leading-none"
803
+ >
804
+ \xD7
805
+ </button>
806
+ </li>
807
+ ))}
808
+ </ul>
809
+ )}
810
+
811
+ {!isPending && todos.length === 0 && (
812
+ <p className="text-sm text-gray-500">No todos yet.</p>
813
+ )}
814
+ </>
815
+ );
816
+ }`
817
+ };
818
+
819
+ // src/web/templates/auth-route.ts
820
+ var auth_route_default = {
821
+ filename: "apps/web/app/api/auth/[...all]/route.ts",
822
+ template: `import { auth } from "@repo/auth";
823
+ import { toNextJsHandler } from "better-auth/next-js";
824
+
825
+ export const { GET, POST } = toNextJsHandler(auth);`
826
+ };
827
+
828
+ // src/web/templates/auth-client.ts
829
+ var auth_client_default = {
830
+ filename: "apps/web/lib/auth-client.ts",
831
+ template: `import { createAuthClient } from "better-auth/react";
832
+
833
+ export const authClient: ReturnType<typeof createAuthClient> = createAuthClient();`
834
+ };
835
+
836
+ // src/web/index.ts
837
+ var web = {
838
+ templates: [
839
+ package_json_default2,
840
+ gitignore_default2,
841
+ readme_default2,
842
+ eslint_config_default,
843
+ next_config_default,
844
+ postcss_config_default,
845
+ tsconfig_default,
846
+ globals_css_default,
847
+ layout_default,
848
+ page_default,
849
+ providers_default,
850
+ auth_form_default,
851
+ todo_list_default,
852
+ auth_route_default,
853
+ auth_client_default
854
+ ],
855
+ staticFiles: [
856
+ { src: "web/static/fonts/GeistVF.woff", filename: "apps/web/app/fonts/GeistVF.woff" },
857
+ { src: "web/static/fonts/GeistMonoVF.woff", filename: "apps/web/app/fonts/GeistMonoVF.woff" },
858
+ { src: "web/static/favicon.ico", filename: "apps/web/app/favicon.ico" },
859
+ { src: "web/static/public/file-text.svg", filename: "apps/web/public/file-text.svg" },
860
+ { src: "web/static/public/globe.svg", filename: "apps/web/public/globe.svg" },
861
+ { src: "web/static/public/next.svg", filename: "apps/web/public/next.svg" },
862
+ { src: "web/static/public/turborepo-dark.svg", filename: "apps/web/public/turborepo-dark.svg" },
863
+ { src: "web/static/public/turborepo-light.svg", filename: "apps/web/public/turborepo-light.svg" },
864
+ { src: "web/static/public/vercel.svg", filename: "apps/web/public/vercel.svg" },
865
+ { src: "web/static/public/window.svg", filename: "apps/web/public/window.svg" }
866
+ ]
867
+ };
868
+ var web_default = web;
869
+
870
+ // src/api/templates/package-json.ts
871
+ var package_json_default3 = {
872
+ filename: "apps/api/package.json",
873
+ template: `{
874
+ "name": "api",
875
+ "version": "0.0.1",
876
+ "description": "",
877
+ "author": "",
878
+ "private": true,
879
+ "license": "UNLICENSED",
880
+ "scripts": {
881
+ "build": "nest build",
882
+ "start": "nest start",
883
+ "dev": "nest start --watch",
884
+ "start:dev": "nest start --watch",
885
+ "start:debug": "nest start --debug --watch",
886
+ "start:prod": "node dist/main",
887
+ "lint": "eslint \\"{src,apps,libs,test}/**/*.ts\\" --fix",
888
+ "test": "jest",
889
+ "test:watch": "jest --watch",
890
+ "test:cov": "jest --coverage",
891
+ "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
892
+ "test:e2e": "jest --config ./test/jest-e2e.json"
893
+ },
894
+ "dependencies": {
895
+ "@nestjs/common": "^11.0.1",
896
+ "@nestjs/core": "^11.0.1",
897
+ "@nestjs/platform-express": "^11.0.1",
898
+ "@repo/auth": "workspace:*",
899
+ "@thallesp/nestjs-better-auth": "^2.5.1",
900
+ "dotenv": "^17.3.1",
901
+ "reflect-metadata": "^0.2.2",
902
+ "rxjs": "^7.8.1"
903
+ },
904
+ "devDependencies": {
905
+ "@eslint/eslintrc": "^3.2.0",
906
+ "@eslint/js": "^9.18.0",
907
+ "@nestjs/cli": "^11.0.0",
908
+ "@nestjs/schematics": "^11.0.0",
909
+ "@nestjs/testing": "^11.0.1",
910
+ "@types/express": "^5.0.0",
911
+ "@types/jest": "^30.0.0",
912
+ "@types/node": "^22.10.7",
913
+ "@types/supertest": "^6.0.2",
914
+ "eslint": "^9.18.0",
915
+ "eslint-config-prettier": "^10.0.1",
916
+ "globals": "^16.0.0",
917
+ "jest": "^30.0.0",
918
+ "source-map-support": "^0.5.21",
919
+ "supertest": "^7.0.0",
920
+ "ts-jest": "^29.2.5",
921
+ "ts-loader": "^9.5.2",
922
+ "ts-node": "^10.9.2",
923
+ "tsconfig-paths": "^4.2.0",
924
+ "typescript": "^5.7.3",
925
+ "typescript-eslint": "^8.20.0"
926
+ },
927
+ "jest": {
928
+ "moduleFileExtensions": ["js", "json", "ts"],
929
+ "rootDir": "src",
930
+ "testRegex": ".*\\\\.spec\\\\.ts$",
931
+ "transform": {
932
+ "^.+\\\\.(t|j)s$": "ts-jest"
933
+ },
934
+ "collectCoverageFrom": ["**/*.(t|j)s"],
935
+ "coverageDirectory": "../coverage",
936
+ "testEnvironment": "node"
937
+ }
938
+ }`
939
+ };
940
+
941
+ // src/api/templates/eslint-config.ts
942
+ var eslint_config_default2 = {
943
+ filename: "apps/api/eslint.config.mjs",
944
+ template: `// @ts-check
945
+ import eslint from '@eslint/js';
946
+ import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
947
+ import globals from 'globals';
948
+ import tseslint from 'typescript-eslint';
949
+
950
+ export default tseslint.config(
951
+ {
952
+ ignores: ['eslint.config.mjs'],
953
+ },
954
+ eslint.configs.recommended,
955
+ ...tseslint.configs.recommendedTypeChecked,
956
+ eslintPluginPrettierRecommended,
957
+ {
958
+ languageOptions: {
959
+ globals: {
960
+ ...globals.node,
961
+ ...globals.jest,
962
+ },
963
+ sourceType: 'commonjs',
964
+ parserOptions: {
965
+ projectService: true,
966
+ tsconfigRootDir: import.meta.dirname,
967
+ },
968
+ },
969
+ },
970
+ {
971
+ rules: {
972
+ '@typescript-eslint/no-explicit-any': 'off',
973
+ '@typescript-eslint/no-floating-promises': 'warn',
974
+ '@typescript-eslint/no-unsafe-argument': 'warn',
975
+ "prettier/prettier": ["error", { endOfLine: "auto" }],
976
+ },
977
+ },
978
+ );`
979
+ };
980
+
981
+ // src/api/templates/nest-cli.ts
982
+ var nest_cli_default = {
983
+ filename: "apps/api/nest-cli.json",
984
+ template: `{
985
+ "$schema": "https://json.schemastore.org/nest-cli",
986
+ "collection": "@nestjs/schematics",
987
+ "sourceRoot": "src",
988
+ "compilerOptions": {
989
+ "deleteOutDir": true
990
+ }
991
+ }`
992
+ };
993
+
994
+ // src/api/templates/tsconfig.ts
995
+ var tsconfig_default2 = {
996
+ filename: "apps/api/tsconfig.json",
997
+ template: `{
998
+ "compilerOptions": {
999
+ "module": "nodenext",
1000
+ "moduleResolution": "nodenext",
1001
+ "resolvePackageJsonExports": true,
1002
+ "esModuleInterop": true,
1003
+ "isolatedModules": true,
1004
+ "declaration": true,
1005
+ "removeComments": true,
1006
+ "emitDecoratorMetadata": true,
1007
+ "experimentalDecorators": true,
1008
+ "allowSyntheticDefaultImports": true,
1009
+ "target": "ES2023",
1010
+ "sourceMap": true,
1011
+ "outDir": "./dist",
1012
+ "baseUrl": "./",
1013
+ "incremental": true,
1014
+ "skipLibCheck": true,
1015
+ "strictNullChecks": true,
1016
+ "forceConsistentCasingInFileNames": true,
1017
+ "noImplicitAny": false,
1018
+ "strictBindCallApply": false,
1019
+ "noFallthroughCasesInSwitch": false
1020
+ }
1021
+ }`
1022
+ };
1023
+
1024
+ // src/api/templates/tsconfig-build.ts
1025
+ var tsconfig_build_default = {
1026
+ filename: "apps/api/tsconfig.build.json",
1027
+ template: `{
1028
+ "extends": "./tsconfig.json",
1029
+ "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
1030
+ }`
1031
+ };
1032
+
1033
+ // src/api/templates/app-controller.ts
1034
+ var app_controller_default = {
1035
+ filename: "apps/api/src/app.controller.ts",
1036
+ template: `import { Controller, Get } from '@nestjs/common';
1037
+ import {
1038
+ AllowAnonymous,
1039
+ Session,
1040
+ UserSession,
1041
+ } from '@thallesp/nestjs-better-auth';
1042
+
1043
+ @Controller()
1044
+ export class AppController {
1045
+ @AllowAnonymous()
1046
+ @Get('hello')
1047
+ getHello(@Session() session: UserSession | null) {
1048
+ if (session?.user.name) {
1049
+ return { message: \`Hello \${session.user.name}\` };
1050
+ }
1051
+
1052
+ return { message: 'Hello from Nest' };
1053
+ }
1054
+ }`
1055
+ };
1056
+
1057
+ // src/api/templates/app-controller-spec.ts
1058
+ var app_controller_spec_default = {
1059
+ filename: "apps/api/src/app.controller.spec.ts",
1060
+ template: `import { Test, TestingModule } from '@nestjs/testing';
1061
+ import { AppController } from './app.controller';
1062
+
1063
+ describe('AppController', () => {
1064
+ let appController: AppController;
1065
+
1066
+ beforeEach(async () => {
1067
+ const app: TestingModule = await Test.createTestingModule({
1068
+ controllers: [AppController],
1069
+ }).compile();
1070
+
1071
+ appController = app.get<AppController>(AppController);
1072
+ });
1073
+
1074
+ it('should be defined', () => {
1075
+ expect(appController).toBeDefined();
1076
+ });
1077
+ });`
1078
+ };
1079
+
1080
+ // src/api/templates/app-module.ts
1081
+ var app_module_default = {
1082
+ filename: "apps/api/src/app.module.ts",
1083
+ template: `import { Module } from '@nestjs/common';
1084
+ import { APP_GUARD } from '@nestjs/core';
1085
+ import { AuthGuard, AuthModule } from '@thallesp/nestjs-better-auth';
1086
+ import { auth } from '@repo/auth';
1087
+ import { AppController } from './app.controller';
1088
+ import { TodosModule } from './todos/todos.module';
1089
+
1090
+ @Module({
1091
+ imports: [AuthModule.forRoot({ auth }), TodosModule],
1092
+ controllers: [AppController],
1093
+ providers: [{ provide: APP_GUARD, useClass: AuthGuard }],
1094
+ })
1095
+ export class AppModule {}`
1096
+ };
1097
+
1098
+ // src/api/templates/main.ts
1099
+ var main_default = {
1100
+ filename: "apps/api/src/main.ts",
1101
+ template: `import 'dotenv/config';
1102
+ import { NestFactory } from '@nestjs/core';
1103
+ import { AppModule } from './app.module';
1104
+
1105
+ async function bootstrap() {
1106
+ const app = await NestFactory.create(AppModule, { bodyParser: false });
1107
+ app.setGlobalPrefix('api');
1108
+ app.enableCors({ origin: 'http://localhost:3000', credentials: true });
1109
+ await app.listen(process.env.PORT ?? 3001);
1110
+ }
1111
+ bootstrap();`
1112
+ };
1113
+
1114
+ // src/api/templates/todos-controller.ts
1115
+ var todos_controller_default = {
1116
+ filename: "apps/api/src/todos/todos.controller.ts",
1117
+ template: `import {
1118
+ Body,
1119
+ Controller,
1120
+ Delete,
1121
+ Get,
1122
+ Param,
1123
+ Patch,
1124
+ Post,
1125
+ } from '@nestjs/common';
1126
+ import { TodosService } from './todos.service';
1127
+
1128
+ @Controller('todos')
1129
+ export class TodosController {
1130
+ constructor(private readonly todosService: TodosService) {}
1131
+
1132
+ @Get()
1133
+ findAll() {
1134
+ return this.todosService.findAll();
1135
+ }
1136
+
1137
+ @Post()
1138
+ create(@Body('title') title: string) {
1139
+ return this.todosService.create(title);
1140
+ }
1141
+
1142
+ @Patch(':id/toggle')
1143
+ toggle(@Param('id') id: string) {
1144
+ return this.todosService.toggle(Number(id));
1145
+ }
1146
+
1147
+ @Delete(':id')
1148
+ remove(@Param('id') id: string) {
1149
+ return this.todosService.remove(Number(id));
1150
+ }
1151
+ }`
1152
+ };
1153
+
1154
+ // src/api/templates/todos-module.ts
1155
+ var todos_module_default = {
1156
+ filename: "apps/api/src/todos/todos.module.ts",
1157
+ template: `import { Module } from '@nestjs/common';
1158
+ import { TodosController } from './todos.controller';
1159
+ import { TodosService } from './todos.service';
1160
+
1161
+ @Module({
1162
+ controllers: [TodosController],
1163
+ providers: [TodosService],
1164
+ })
1165
+ export class TodosModule {}`
1166
+ };
1167
+
1168
+ // src/api/templates/todos-service.ts
1169
+ var todos_service_default = {
1170
+ filename: "apps/api/src/todos/todos.service.ts",
1171
+ template: `import { Injectable, NotFoundException } from '@nestjs/common';
1172
+
1173
+ export interface Todo {
1174
+ id: number;
1175
+ title: string;
1176
+ done: boolean;
1177
+ }
1178
+
1179
+ @Injectable()
1180
+ export class TodosService {
1181
+ private todos: Todo[] = [];
1182
+ private nextId = 1;
1183
+
1184
+ findAll(): Todo[] {
1185
+ return this.todos;
1186
+ }
1187
+
1188
+ create(title: string): Todo {
1189
+ const todo: Todo = { id: this.nextId++, title, done: false };
1190
+ this.todos.push(todo);
1191
+ return todo;
1192
+ }
1193
+
1194
+ toggle(id: number): Todo {
1195
+ const todo = this.todos.find((t) => t.id === id);
1196
+ if (!todo) throw new NotFoundException(\`Todo \${id} not found\`);
1197
+ todo.done = !todo.done;
1198
+ return todo;
1199
+ }
1200
+
1201
+ remove(id: number): void {
1202
+ const index = this.todos.findIndex((t) => t.id === id);
1203
+ if (index === -1) throw new NotFoundException(\`Todo \${id} not found\`);
1204
+ this.todos.splice(index, 1);
1205
+ }
1206
+ }`
1207
+ };
1208
+
1209
+ // src/api/templates/e2e-spec.ts
1210
+ var e2e_spec_default = {
1211
+ filename: "apps/api/test/app.e2e-spec.ts",
1212
+ template: `import { Test, TestingModule } from '@nestjs/testing';
1213
+ import { INestApplication } from '@nestjs/common';
1214
+ import request from 'supertest';
1215
+ import { App } from 'supertest/types';
1216
+ import { AppModule } from './../src/app.module';
1217
+
1218
+ describe('AppController (e2e)', () => {
1219
+ let app: INestApplication<App>;
1220
+
1221
+ beforeEach(async () => {
1222
+ const moduleFixture: TestingModule = await Test.createTestingModule({
1223
+ imports: [AppModule],
1224
+ }).compile();
1225
+
1226
+ app = moduleFixture.createNestApplication();
1227
+ await app.init();
1228
+ });
1229
+
1230
+ it('/ (GET)', () => {
1231
+ return request(app.getHttpServer())
1232
+ .get('/')
1233
+ .expect(200)
1234
+ .expect('Hello World!');
1235
+ });
1236
+ });`
1237
+ };
1238
+
1239
+ // src/api/templates/jest-e2e.ts
1240
+ var jest_e2e_default = {
1241
+ filename: "apps/api/test/jest-e2e.json",
1242
+ template: `{
1243
+ "moduleFileExtensions": ["js", "json", "ts"],
1244
+ "rootDir": ".",
1245
+ "testEnvironment": "node",
1246
+ "testRegex": ".e2e-spec.ts$",
1247
+ "transform": {
1248
+ "^.+\\\\.(t|j)s$": "ts-jest"
1249
+ }
1250
+ }`
1251
+ };
1252
+
1253
+ // src/api/templates/readme.ts
1254
+ var readme_default3 = {
1255
+ filename: "apps/api/README.md",
1256
+ template: `# API
1257
+
1258
+ NestJS backend for the monorepo.
1259
+
1260
+ ## Scripts
1261
+
1262
+ \`\`\`sh
1263
+ pnpm dev # watch mode
1264
+ pnpm build # production build
1265
+ pnpm test # unit tests
1266
+ pnpm test:e2e # e2e tests
1267
+ \`\`\``
1268
+ };
1269
+
1270
+ // src/api/index.ts
1271
+ var api = {
1272
+ templates: [
1273
+ package_json_default3,
1274
+ eslint_config_default2,
1275
+ nest_cli_default,
1276
+ tsconfig_default2,
1277
+ tsconfig_build_default,
1278
+ app_controller_default,
1279
+ app_controller_spec_default,
1280
+ app_module_default,
1281
+ main_default,
1282
+ todos_controller_default,
1283
+ todos_module_default,
1284
+ todos_service_default,
1285
+ e2e_spec_default,
1286
+ jest_e2e_default,
1287
+ readme_default3
1288
+ ]
1289
+ };
1290
+ var api_default = api;
1291
+
1292
+ // src/auth/templates/package-json.ts
1293
+ var package_json_default4 = {
1294
+ filename: "packages/auth/package.json",
1295
+ template: `{
1296
+ "name": "@repo/auth",
1297
+ "version": "0.0.0",
1298
+ "private": true,
1299
+ "exports": {
1300
+ ".": "./src/index.ts"
1301
+ },
1302
+ "scripts": {
1303
+ "migrate": "dotenv -e .env -- auth migrate --config src/index.ts",
1304
+ "generate": "dotenv -e .env -- auth generate --config src/index.ts"
1305
+ },
1306
+ "dependencies": {
1307
+ "better-auth": "^1.2.8",
1308
+ "pg": "^8.14.1"
1309
+ },
1310
+ "devDependencies": {
1311
+ "@repo/typescript-config": "workspace:*",
1312
+ "@types/pg": "^8.11.13",
1313
+ "auth": "^1.5.5",
1314
+ "dotenv-cli": "^11.0.0",
1315
+ "typescript": "5.9.2"
1316
+ }
1317
+ }`
1318
+ };
1319
+
1320
+ // src/auth/templates/src-index.ts
1321
+ var src_index_default = {
1322
+ filename: "packages/auth/src/index.ts",
1323
+ template: `import { betterAuth } from "better-auth";
1324
+ import { Pool } from "pg";
1325
+
1326
+ export const auth = betterAuth({
1327
+ database: new Pool({
1328
+ connectionString: process.env.DATABASE_URL,
1329
+ }),
1330
+ emailAndPassword: { enabled: true },
1331
+ trustedOrigins: [process.env.BETTER_AUTH_URL ?? "http://localhost:3000"],
1332
+ });
1333
+
1334
+ export type Auth = typeof auth;`
1335
+ };
1336
+
1337
+ // src/auth/templates/tsconfig.ts
1338
+ var tsconfig_default3 = {
1339
+ filename: "packages/auth/tsconfig.json",
1340
+ template: `{
1341
+ "extends": "@repo/typescript-config/base.json",
1342
+ "compilerOptions": {
1343
+ "outDir": "dist",
1344
+ "strictNullChecks": true
1345
+ },
1346
+ "include": ["src"],
1347
+ "exclude": ["node_modules", "dist"]
1348
+ }`
1349
+ };
1350
+
1351
+ // src/auth/index.ts
1352
+ var auth = {
1353
+ templates: [package_json_default4, src_index_default, tsconfig_default3]
1354
+ };
1355
+ var auth_default = auth;
1356
+
1357
+ // src/ui/templates/package-json.ts
1358
+ var package_json_default5 = {
1359
+ filename: "packages/ui/package.json",
1360
+ template: `{
1361
+ "name": "@repo/ui",
1362
+ "version": "0.0.0",
1363
+ "private": true,
1364
+ "exports": {
1365
+ "./*": "./src/*.tsx"
1366
+ },
1367
+ "scripts": {
1368
+ "lint": "eslint . --max-warnings 0",
1369
+ "check-types": "tsc --noEmit"
1370
+ },
1371
+ "devDependencies": {
1372
+ "@repo/eslint-config": "workspace:*",
1373
+ "@repo/typescript-config": "workspace:*",
1374
+ "@types/node": "^22.15.3",
1375
+ "@types/react": "19.2.2",
1376
+ "@types/react-dom": "19.2.2",
1377
+ "eslint": "^9.39.1",
1378
+ "typescript": "5.9.2"
1379
+ },
1380
+ "dependencies": {
1381
+ "react": "^19.2.0",
1382
+ "react-dom": "^19.2.0"
1383
+ }
1384
+ }`
1385
+ };
1386
+
1387
+ // src/ui/templates/button.ts
1388
+ var button_default = {
1389
+ filename: "packages/ui/src/button.tsx",
1390
+ template: `"use client";
1391
+
1392
+ import { ReactNode } from "react";
1393
+
1394
+ interface ButtonProps {
1395
+ children: ReactNode;
1396
+ className?: string;
1397
+ appName: string;
1398
+ }
1399
+
1400
+ export const Button = ({ children, className, appName }: ButtonProps) => {
1401
+ return (
1402
+ <button
1403
+ className={className}
1404
+ onClick={() => alert(\`Hello from your \${appName} app!\`)}
1405
+ >
1406
+ {children}
1407
+ </button>
1408
+ );
1409
+ };`
1410
+ };
1411
+
1412
+ // src/ui/templates/card.ts
1413
+ var card_default = {
1414
+ filename: "packages/ui/src/card.tsx",
1415
+ template: `import { type JSX } from "react";
1416
+
1417
+ export function Card({
1418
+ className,
1419
+ title,
1420
+ children,
1421
+ href,
1422
+ }: {
1423
+ className?: string;
1424
+ title: string;
1425
+ children: React.ReactNode;
1426
+ href: string;
1427
+ }): JSX.Element {
1428
+ return (
1429
+ <a
1430
+ className={className}
1431
+ href={\`\${href}?utm_source=create-turbo&utm_medium=basic&utm_campaign=create-turbo"\`}
1432
+ rel="noopener noreferrer"
1433
+ target="_blank"
1434
+ >
1435
+ <h2>
1436
+ {title} <span>-&gt;</span>
1437
+ </h2>
1438
+ <p>{children}</p>
1439
+ </a>
1440
+ );
1441
+ }`
1442
+ };
1443
+
1444
+ // src/ui/templates/code.ts
1445
+ var code_default = {
1446
+ filename: "packages/ui/src/code.tsx",
1447
+ template: `import { type JSX } from "react";
1448
+
1449
+ export function Code({
1450
+ children,
1451
+ className,
1452
+ }: {
1453
+ children: React.ReactNode;
1454
+ className?: string;
1455
+ }): JSX.Element {
1456
+ return <code className={className}>{children}</code>;
1457
+ }`
1458
+ };
1459
+
1460
+ // src/ui/templates/link.ts
1461
+ var link_default = {
1462
+ filename: "packages/ui/src/link.tsx",
1463
+ template: `"use client";
1464
+
1465
+ import { AnchorHTMLAttributes } from "react";
1466
+
1467
+ export function Link({ children, ...props }: AnchorHTMLAttributes<HTMLAnchorElement>) {
1468
+ return (
1469
+ <a
1470
+ target="_blank"
1471
+ rel="noopener noreferrer"
1472
+ className="hover:underline underline-offset-2 hover:text-gray-900 transition-colors"
1473
+ {...props}
1474
+ >
1475
+ {children}
1476
+ </a>
1477
+ );
1478
+ }`
1479
+ };
1480
+
1481
+ // src/ui/templates/tsconfig.ts
1482
+ var tsconfig_default4 = {
1483
+ filename: "packages/ui/tsconfig.json",
1484
+ template: `{
1485
+ "extends": "@repo/typescript-config/react-library.json",
1486
+ "compilerOptions": {
1487
+ "outDir": "dist",
1488
+ "strictNullChecks": true
1489
+ },
1490
+ "include": ["src"],
1491
+ "exclude": ["node_modules", "dist"]
1492
+ }`
1493
+ };
1494
+
1495
+ // src/ui/templates/eslint-config.ts
1496
+ var eslint_config_default3 = {
1497
+ filename: "packages/ui/eslint.config.mjs",
1498
+ template: `import { config } from "@repo/eslint-config/react-internal";
1499
+
1500
+ /** @type {import("eslint").Linter.Config} */
1501
+ export default config;`
1502
+ };
1503
+
1504
+ // src/ui/index.ts
1505
+ var ui = {
1506
+ templates: [package_json_default5, button_default, card_default, code_default, link_default, tsconfig_default4, eslint_config_default3]
1507
+ };
1508
+ var ui_default = ui;
1509
+
1510
+ // src/eslint-config/templates/package-json.ts
1511
+ var package_json_default6 = {
1512
+ filename: "packages/eslint-config/package.json",
1513
+ template: `{
1514
+ "name": "@repo/eslint-config",
1515
+ "version": "0.0.0",
1516
+ "type": "module",
1517
+ "private": true,
1518
+ "exports": {
1519
+ "./base": "./base.js",
1520
+ "./next-js": "./next.js",
1521
+ "./react-internal": "./react-internal.js"
1522
+ },
1523
+ "devDependencies": {
1524
+ "@eslint/js": "^9.39.1",
1525
+ "@next/eslint-plugin-next": "^15.5.0",
1526
+ "eslint": "^9.39.1",
1527
+ "eslint-config-prettier": "^10.1.1",
1528
+ "eslint-plugin-only-warn": "^1.1.0",
1529
+ "eslint-plugin-react": "^7.37.5",
1530
+ "eslint-plugin-react-hooks": "^5.2.0",
1531
+ "eslint-plugin-turbo": "^2.7.1",
1532
+ "globals": "^16.5.0",
1533
+ "typescript": "^5.9.2",
1534
+ "typescript-eslint": "^8.50.0"
1535
+ }
1536
+ }`
1537
+ };
1538
+
1539
+ // src/eslint-config/templates/base.ts
1540
+ var base_default = {
1541
+ filename: "packages/eslint-config/base.js",
1542
+ template: `import js from "@eslint/js";
1543
+ import eslintConfigPrettier from "eslint-config-prettier";
1544
+ import turboPlugin from "eslint-plugin-turbo";
1545
+ import tseslint from "typescript-eslint";
1546
+ import onlyWarn from "eslint-plugin-only-warn";
1547
+
1548
+ /**
1549
+ * A shared ESLint configuration for the repository.
1550
+ *
1551
+ * @type {import("eslint").Linter.Config[]}
1552
+ * */
1553
+ export const config = [
1554
+ js.configs.recommended,
1555
+ eslintConfigPrettier,
1556
+ ...tseslint.configs.recommended,
1557
+ {
1558
+ plugins: {
1559
+ turbo: turboPlugin,
1560
+ },
1561
+ rules: {
1562
+ "turbo/no-undeclared-env-vars": "warn",
1563
+ },
1564
+ },
1565
+ {
1566
+ plugins: {
1567
+ onlyWarn,
1568
+ },
1569
+ },
1570
+ {
1571
+ ignores: ["dist/**"],
1572
+ },
1573
+ ];`
1574
+ };
1575
+
1576
+ // src/eslint-config/templates/next.ts
1577
+ var next_default = {
1578
+ filename: "packages/eslint-config/next.js",
1579
+ template: `import js from "@eslint/js";
1580
+ import { globalIgnores } from "eslint/config";
1581
+ import eslintConfigPrettier from "eslint-config-prettier";
1582
+ import tseslint from "typescript-eslint";
1583
+ import pluginReactHooks from "eslint-plugin-react-hooks";
1584
+ import pluginReact from "eslint-plugin-react";
1585
+ import globals from "globals";
1586
+ import pluginNext from "@next/eslint-plugin-next";
1587
+ import { config as baseConfig } from "./base.js";
1588
+
1589
+ /**
1590
+ * A custom ESLint configuration for libraries that use Next.js.
1591
+ *
1592
+ * @type {import("eslint").Linter.Config[]}
1593
+ * */
1594
+ export const nextJsConfig = [
1595
+ ...baseConfig,
1596
+ js.configs.recommended,
1597
+ eslintConfigPrettier,
1598
+ ...tseslint.configs.recommended,
1599
+ globalIgnores([
1600
+ // Default ignores of eslint-config-next:
1601
+ ".next/**",
1602
+ "out/**",
1603
+ "build/**",
1604
+ "next-env.d.ts",
1605
+ ]),
1606
+ {
1607
+ ...pluginReact.configs.flat.recommended,
1608
+ languageOptions: {
1609
+ ...pluginReact.configs.flat.recommended.languageOptions,
1610
+ globals: {
1611
+ ...globals.serviceworker,
1612
+ },
1613
+ },
1614
+ },
1615
+ {
1616
+ plugins: {
1617
+ "@next/next": pluginNext,
1618
+ },
1619
+ rules: {
1620
+ ...pluginNext.configs.recommended.rules,
1621
+ ...pluginNext.configs["core-web-vitals"].rules,
1622
+ },
1623
+ },
1624
+ {
1625
+ plugins: {
1626
+ "react-hooks": pluginReactHooks,
1627
+ },
1628
+ settings: { react: { version: "detect" } },
1629
+ rules: {
1630
+ ...pluginReactHooks.configs.recommended.rules,
1631
+ // React scope no longer necessary with new JSX transform.
1632
+ "react/react-in-jsx-scope": "off",
1633
+ },
1634
+ },
1635
+ ];`
1636
+ };
1637
+
1638
+ // src/eslint-config/templates/react-internal.ts
1639
+ var react_internal_default = {
1640
+ filename: "packages/eslint-config/react-internal.js",
1641
+ template: `import js from "@eslint/js";
1642
+ import eslintConfigPrettier from "eslint-config-prettier";
1643
+ import tseslint from "typescript-eslint";
1644
+ import pluginReactHooks from "eslint-plugin-react-hooks";
1645
+ import pluginReact from "eslint-plugin-react";
1646
+ import globals from "globals";
1647
+ import { config as baseConfig } from "./base.js";
1648
+
1649
+ /**
1650
+ * A custom ESLint configuration for libraries that use React.
1651
+ *
1652
+ * @type {import("eslint").Linter.Config[]} */
1653
+ export const config = [
1654
+ ...baseConfig,
1655
+ js.configs.recommended,
1656
+ eslintConfigPrettier,
1657
+ ...tseslint.configs.recommended,
1658
+ pluginReact.configs.flat.recommended,
1659
+ {
1660
+ languageOptions: {
1661
+ ...pluginReact.configs.flat.recommended.languageOptions,
1662
+ globals: {
1663
+ ...globals.serviceworker,
1664
+ ...globals.browser,
1665
+ },
1666
+ },
1667
+ },
1668
+ {
1669
+ plugins: {
1670
+ "react-hooks": pluginReactHooks,
1671
+ },
1672
+ settings: { react: { version: "detect" } },
1673
+ rules: {
1674
+ ...pluginReactHooks.configs.recommended.rules,
1675
+ // React scope no longer necessary with new JSX transform.
1676
+ "react/react-in-jsx-scope": "off",
1677
+ },
1678
+ },
1679
+ ];`
1680
+ };
1681
+
1682
+ // src/eslint-config/index.ts
1683
+ var eslintConfig = {
1684
+ templates: [package_json_default6, base_default, next_default, react_internal_default]
1685
+ };
1686
+ var eslint_config_default4 = eslintConfig;
1687
+
1688
+ // src/typescript-config/templates/package-json.ts
1689
+ var package_json_default7 = {
1690
+ filename: "packages/typescript-config/package.json",
1691
+ template: `{
1692
+ "name": "@repo/typescript-config",
1693
+ "version": "0.0.0",
1694
+ "private": true,
1695
+ "license": "MIT",
1696
+ "publishConfig": {
1697
+ "access": "public"
1698
+ }
1699
+ }`
1700
+ };
1701
+
1702
+ // src/typescript-config/templates/base.ts
1703
+ var base_default2 = {
1704
+ filename: "packages/typescript-config/base.json",
1705
+ template: `{
1706
+ "$schema": "https://json.schemastore.org/tsconfig",
1707
+ "compilerOptions": {
1708
+ "declaration": true,
1709
+ "declarationMap": true,
1710
+ "esModuleInterop": true,
1711
+ "incremental": false,
1712
+ "isolatedModules": true,
1713
+ "lib": ["es2022", "DOM", "DOM.Iterable"],
1714
+ "module": "NodeNext",
1715
+ "moduleDetection": "force",
1716
+ "moduleResolution": "NodeNext",
1717
+ "noUncheckedIndexedAccess": true,
1718
+ "resolveJsonModule": true,
1719
+ "skipLibCheck": true,
1720
+ "strict": true,
1721
+ "target": "ES2022"
1722
+ }
1723
+ }`
1724
+ };
1725
+
1726
+ // src/typescript-config/templates/nextjs.ts
1727
+ var nextjs_default = {
1728
+ filename: "packages/typescript-config/nextjs.json",
1729
+ template: `{
1730
+ "$schema": "https://json.schemastore.org/tsconfig",
1731
+ "extends": "./base.json",
1732
+ "compilerOptions": {
1733
+ "plugins": [{ "name": "next" }],
1734
+ "module": "ESNext",
1735
+ "moduleResolution": "Bundler",
1736
+ "allowJs": true,
1737
+ "jsx": "preserve",
1738
+ "noEmit": true
1739
+ }
1740
+ }`
1741
+ };
1742
+
1743
+ // src/typescript-config/templates/react-library.ts
1744
+ var react_library_default = {
1745
+ filename: "packages/typescript-config/react-library.json",
1746
+ template: `{
1747
+ "$schema": "https://json.schemastore.org/tsconfig",
1748
+ "extends": "./base.json",
1749
+ "compilerOptions": {
1750
+ "jsx": "react-jsx"
1751
+ }
1752
+ }`
1753
+ };
1754
+
1755
+ // src/typescript-config/index.ts
1756
+ var typescriptConfig = {
1757
+ templates: [package_json_default7, base_default2, nextjs_default, react_library_default]
1758
+ };
1759
+ var typescript_config_default = typescriptConfig;
1760
+
1761
+ // src/index.ts
1762
+ var staticDir = new URL("./static/", import.meta.url);
1763
+ var staticDirPath = fileURLToPath(staticDir);
1764
+ function getStaticFilePath(name) {
1765
+ return fileURLToPath(new URL(`./static/${name}`, import.meta.url));
1766
+ }
1767
+ var templates = {
1768
+ root: root_default,
1769
+ web: web_default,
1770
+ api: api_default,
1771
+ auth: auth_default,
1772
+ ui: ui_default,
1773
+ eslintConfig: eslint_config_default4,
1774
+ typescriptConfig: typescript_config_default
1775
+ };
1776
+ export {
1777
+ getStaticFilePath,
1778
+ staticDir,
1779
+ staticDirPath,
1780
+ templates
1781
+ };