@newt-app/templates 0.16.0 → 0.17.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.
- package/dist/index.d.ts +5 -0
- package/dist/index.js +737 -500
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -47,6 +47,11 @@ declare const templates: {
|
|
|
47
47
|
deploymentSpa: Module;
|
|
48
48
|
nestDiOnly: Module;
|
|
49
49
|
apiControllers: Module;
|
|
50
|
+
todoExampleApi: Module;
|
|
51
|
+
todoExampleControllers: Module;
|
|
52
|
+
todoExampleDi: Module;
|
|
53
|
+
todoExampleWeb: Module;
|
|
54
|
+
todoExampleShadcn: Module;
|
|
50
55
|
};
|
|
51
56
|
|
|
52
57
|
export { type File, type Module, type Package, type Script, type Template, type TemplateData, getStaticFilePath, staticDir, staticDirPath, templates };
|
package/dist/index.js
CHANGED
|
@@ -459,9 +459,9 @@ var page_default = {
|
|
|
459
459
|
import { useQuery } from '@tanstack/react-query';
|
|
460
460
|
import { authClient } from '@/lib/auth-client';
|
|
461
461
|
import { AuthForm } from '@/app/auth-form';
|
|
462
|
+
import { Button } from '@<%= projectName %>/ui/button';
|
|
462
463
|
import { Link } from '@<%= projectName %>/ui/link';
|
|
463
464
|
import { Logo } from '@<%= projectName %>/ui/logo';
|
|
464
|
-
import { TodoList } from '@/app/todo-list';
|
|
465
465
|
|
|
466
466
|
export default function Home() {
|
|
467
467
|
const { data: session, isPending } = authClient.useSession();
|
|
@@ -507,7 +507,12 @@ export default function Home() {
|
|
|
507
507
|
{isPending ? (
|
|
508
508
|
<p className="text-sm text-gray-400">Loading\u2026</p>
|
|
509
509
|
) : session ? (
|
|
510
|
-
<
|
|
510
|
+
<div className="space-y-3">
|
|
511
|
+
<p className="text-sm">Signed in as {session.user.name}</p>
|
|
512
|
+
<Button onClick={() => void authClient.signOut()}>
|
|
513
|
+
Sign out
|
|
514
|
+
</Button>
|
|
515
|
+
</div>
|
|
511
516
|
) : (
|
|
512
517
|
<AuthForm />
|
|
513
518
|
)}
|
|
@@ -674,149 +679,6 @@ export function AuthForm() {
|
|
|
674
679
|
}`
|
|
675
680
|
};
|
|
676
681
|
|
|
677
|
-
// src/web/templates/todo-list.ts
|
|
678
|
-
var todo_list_default = {
|
|
679
|
-
filename: "apps/web/app/todo-list.tsx",
|
|
680
|
-
template: `'use client';
|
|
681
|
-
|
|
682
|
-
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
683
|
-
import { useForm } from '@tanstack/react-form';
|
|
684
|
-
import { authClient } from '@/lib/auth-client';
|
|
685
|
-
import { Button } from '@<%= projectName %>/ui/button';
|
|
686
|
-
|
|
687
|
-
interface Todo {
|
|
688
|
-
id: number;
|
|
689
|
-
title: string;
|
|
690
|
-
done: boolean;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
const api = {
|
|
694
|
-
getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
|
|
695
|
-
createTodo: (title: string): Promise<Todo> =>
|
|
696
|
-
fetch('/api/todos', {
|
|
697
|
-
method: 'POST',
|
|
698
|
-
headers: { 'Content-Type': 'application/json' },
|
|
699
|
-
body: JSON.stringify({ title }),
|
|
700
|
-
}).then((r) => r.json()),
|
|
701
|
-
toggleTodo: (id: number): Promise<Todo> =>
|
|
702
|
-
fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
|
|
703
|
-
deleteTodo: (id: number): Promise<void> =>
|
|
704
|
-
fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
|
|
705
|
-
};
|
|
706
|
-
|
|
707
|
-
export function TodoList({
|
|
708
|
-
session,
|
|
709
|
-
}: {
|
|
710
|
-
session: { user: { email: string } };
|
|
711
|
-
}) {
|
|
712
|
-
const queryClient = useQueryClient();
|
|
713
|
-
|
|
714
|
-
const { data: todos = [], isPending } = useQuery({
|
|
715
|
-
queryKey: ['todos'],
|
|
716
|
-
queryFn: api.getTodos,
|
|
717
|
-
});
|
|
718
|
-
|
|
719
|
-
const createMutation = useMutation({
|
|
720
|
-
mutationFn: api.createTodo,
|
|
721
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
722
|
-
});
|
|
723
|
-
|
|
724
|
-
const toggleMutation = useMutation({
|
|
725
|
-
mutationFn: api.toggleTodo,
|
|
726
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
const deleteMutation = useMutation({
|
|
730
|
-
mutationFn: api.deleteTodo,
|
|
731
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
732
|
-
});
|
|
733
|
-
|
|
734
|
-
const form = useForm({
|
|
735
|
-
defaultValues: { title: '' },
|
|
736
|
-
onSubmit: async ({ value }) => {
|
|
737
|
-
if (!value.title.trim()) return;
|
|
738
|
-
createMutation.mutate(value.title.trim());
|
|
739
|
-
form.reset();
|
|
740
|
-
},
|
|
741
|
-
});
|
|
742
|
-
|
|
743
|
-
return (
|
|
744
|
-
<>
|
|
745
|
-
<div className="flex items-center justify-between mb-8">
|
|
746
|
-
<h1 className="text-2xl font-semibold">Todos</h1>
|
|
747
|
-
<div className="flex items-center gap-3 text-sm text-gray-400">
|
|
748
|
-
<span>{session.user.email}</span>
|
|
749
|
-
<button
|
|
750
|
-
className="hover:text-gray-100"
|
|
751
|
-
onClick={() => authClient.signOut()}
|
|
752
|
-
>
|
|
753
|
-
Sign out
|
|
754
|
-
</button>
|
|
755
|
-
</div>
|
|
756
|
-
</div>
|
|
757
|
-
|
|
758
|
-
<form
|
|
759
|
-
onSubmit={(e) => {
|
|
760
|
-
e.preventDefault();
|
|
761
|
-
form.handleSubmit();
|
|
762
|
-
}}
|
|
763
|
-
className="flex gap-2 mb-6"
|
|
764
|
-
>
|
|
765
|
-
<form.Field name="title">
|
|
766
|
-
{(field) => (
|
|
767
|
-
<input
|
|
768
|
-
value={field.state.value}
|
|
769
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
770
|
-
placeholder="New todo\u2026"
|
|
771
|
-
className="flex h-9 flex-1 rounded-md border border-neutral-700 bg-neutral-800/50 px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-neutral-500 focus-visible:border-neutral-500 focus-visible:ring-2 focus-visible:ring-neutral-500/20"
|
|
772
|
-
/>
|
|
773
|
-
)}
|
|
774
|
-
</form.Field>
|
|
775
|
-
|
|
776
|
-
<form.Subscribe selector={(s) => s.isSubmitting}>
|
|
777
|
-
{(isSubmitting) => (
|
|
778
|
-
<Button type="submit" disabled={isSubmitting || createMutation.isPending}>
|
|
779
|
-
Add
|
|
780
|
-
</Button>
|
|
781
|
-
)}
|
|
782
|
-
</form.Subscribe>
|
|
783
|
-
</form>
|
|
784
|
-
|
|
785
|
-
{isPending ? (
|
|
786
|
-
<p className="text-sm text-gray-400">Loading\u2026</p>
|
|
787
|
-
) : (
|
|
788
|
-
<ul className="divide-y divide-neutral-700">
|
|
789
|
-
{todos.map((todo) => (
|
|
790
|
-
<li key={todo.id} className="flex items-center gap-3 py-3">
|
|
791
|
-
<input
|
|
792
|
-
type="checkbox"
|
|
793
|
-
checked={todo.done}
|
|
794
|
-
onChange={() => toggleMutation.mutate(todo.id)}
|
|
795
|
-
/>
|
|
796
|
-
<span
|
|
797
|
-
className={\`flex-1 text-sm \${todo.done ? 'line-through text-gray-400' : ''}\`}
|
|
798
|
-
>
|
|
799
|
-
{todo.title}
|
|
800
|
-
</span>
|
|
801
|
-
<button
|
|
802
|
-
onClick={() => deleteMutation.mutate(todo.id)}
|
|
803
|
-
className="text-gray-600 hover:text-red-400 text-lg leading-none"
|
|
804
|
-
>
|
|
805
|
-
\xD7
|
|
806
|
-
</button>
|
|
807
|
-
</li>
|
|
808
|
-
))}
|
|
809
|
-
</ul>
|
|
810
|
-
)}
|
|
811
|
-
|
|
812
|
-
{!isPending && todos.length === 0 && (
|
|
813
|
-
<p className="text-sm text-gray-400">No todos yet.</p>
|
|
814
|
-
)}
|
|
815
|
-
</>
|
|
816
|
-
);
|
|
817
|
-
}`
|
|
818
|
-
};
|
|
819
|
-
|
|
820
682
|
// src/web/templates/auth-route.ts
|
|
821
683
|
var auth_route_default = {
|
|
822
684
|
filename: "apps/web/app/api/auth/[...all]/route.ts",
|
|
@@ -874,7 +736,6 @@ var web = {
|
|
|
874
736
|
page_default,
|
|
875
737
|
providers_default,
|
|
876
738
|
auth_form_default,
|
|
877
|
-
todo_list_default,
|
|
878
739
|
auth_route_default,
|
|
879
740
|
auth_client_default,
|
|
880
741
|
manifest_default
|
|
@@ -1047,10 +908,8 @@ var app_module_default = {
|
|
|
1047
908
|
filename: "apps/api/src/app.module.ts",
|
|
1048
909
|
template: `import { Module } from '@nestjs/common';
|
|
1049
910
|
import { AppService } from './app.service';
|
|
1050
|
-
import { TodosModule } from './todos/todos.module';
|
|
1051
911
|
|
|
1052
912
|
@Module({
|
|
1053
|
-
imports: [TodosModule],
|
|
1054
913
|
providers: [AppService],
|
|
1055
914
|
})
|
|
1056
915
|
export class AppModule {}`
|
|
@@ -1092,83 +951,6 @@ describe('AppService', () => {
|
|
|
1092
951
|
});`
|
|
1093
952
|
};
|
|
1094
953
|
|
|
1095
|
-
// src/api/templates/todos-module.ts
|
|
1096
|
-
var todos_module_default = {
|
|
1097
|
-
filename: "apps/api/src/todos/todos.module.ts",
|
|
1098
|
-
template: `import { Module } from '@nestjs/common';
|
|
1099
|
-
import { TodosService } from './todos.service';
|
|
1100
|
-
|
|
1101
|
-
@Module({
|
|
1102
|
-
providers: [TodosService],
|
|
1103
|
-
exports: [TodosService],
|
|
1104
|
-
})
|
|
1105
|
-
export class TodosModule {}`
|
|
1106
|
-
};
|
|
1107
|
-
|
|
1108
|
-
// src/api/templates/todos-service.ts
|
|
1109
|
-
var todos_service_default = {
|
|
1110
|
-
filename: "apps/api/src/todos/todos.service.ts",
|
|
1111
|
-
template: `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
1112
|
-
|
|
1113
|
-
export interface Todo {
|
|
1114
|
-
id: number;
|
|
1115
|
-
title: string;
|
|
1116
|
-
done: boolean;
|
|
1117
|
-
}
|
|
1118
|
-
|
|
1119
|
-
@Injectable()
|
|
1120
|
-
export class TodosService {
|
|
1121
|
-
private todos: Todo[] = [];
|
|
1122
|
-
private nextId = 1;
|
|
1123
|
-
|
|
1124
|
-
findAll(): Todo[] {
|
|
1125
|
-
return this.todos;
|
|
1126
|
-
}
|
|
1127
|
-
|
|
1128
|
-
create(title: string): Todo {
|
|
1129
|
-
const todo: Todo = { id: this.nextId++, title, done: false };
|
|
1130
|
-
this.todos.push(todo);
|
|
1131
|
-
return todo;
|
|
1132
|
-
}
|
|
1133
|
-
|
|
1134
|
-
toggle(id: number): Todo {
|
|
1135
|
-
const todo = this.todos.find((t) => t.id === id);
|
|
1136
|
-
if (!todo) throw new NotFoundException(\`Todo \${id} not found\`);
|
|
1137
|
-
todo.done = !todo.done;
|
|
1138
|
-
return todo;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
remove(id: number): void {
|
|
1142
|
-
const index = this.todos.findIndex((t) => t.id === id);
|
|
1143
|
-
if (index === -1) throw new NotFoundException(\`Todo \${id} not found\`);
|
|
1144
|
-
this.todos.splice(index, 1);
|
|
1145
|
-
}
|
|
1146
|
-
}`
|
|
1147
|
-
};
|
|
1148
|
-
|
|
1149
|
-
// src/api/templates/todos-service-spec.ts
|
|
1150
|
-
var todos_service_spec_default = {
|
|
1151
|
-
filename: "apps/api/src/todos/todos.service.spec.ts",
|
|
1152
|
-
template: `import { Test, TestingModule } from '@nestjs/testing';
|
|
1153
|
-
import { TodosService } from './todos.service';
|
|
1154
|
-
|
|
1155
|
-
describe('TodosService', () => {
|
|
1156
|
-
let service: TodosService;
|
|
1157
|
-
|
|
1158
|
-
beforeEach(async () => {
|
|
1159
|
-
const module: TestingModule = await Test.createTestingModule({
|
|
1160
|
-
providers: [TodosService],
|
|
1161
|
-
}).compile();
|
|
1162
|
-
|
|
1163
|
-
service = module.get<TodosService>(TodosService);
|
|
1164
|
-
});
|
|
1165
|
-
|
|
1166
|
-
it('should be defined', () => {
|
|
1167
|
-
expect(service).toBeDefined();
|
|
1168
|
-
});
|
|
1169
|
-
});`
|
|
1170
|
-
};
|
|
1171
|
-
|
|
1172
954
|
// src/api/templates/readme.ts
|
|
1173
955
|
var readme_default3 = {
|
|
1174
956
|
filename: "apps/api/README.md",
|
|
@@ -1197,9 +979,6 @@ var api = {
|
|
|
1197
979
|
app_module_default,
|
|
1198
980
|
app_service_default,
|
|
1199
981
|
app_service_spec_default,
|
|
1200
|
-
todos_module_default,
|
|
1201
|
-
todos_service_default,
|
|
1202
|
-
todos_service_spec_default,
|
|
1203
982
|
readme_default3
|
|
1204
983
|
]
|
|
1205
984
|
};
|
|
@@ -1943,9 +1722,9 @@ var page_default2 = {
|
|
|
1943
1722
|
import { useQuery } from '@tanstack/react-query';
|
|
1944
1723
|
import { authClient } from '@/lib/auth-client';
|
|
1945
1724
|
import { AuthForm } from '@/app/auth-form';
|
|
1725
|
+
import { Button } from '@<%= projectName %>/ui/button';
|
|
1946
1726
|
import { Link } from '@<%= projectName %>/ui/link';
|
|
1947
1727
|
import { Logo } from '@<%= projectName %>/ui/logo';
|
|
1948
|
-
import { TodoList } from '@/app/todo-list';
|
|
1949
1728
|
import { ModeToggle } from '@<%= projectName %>/ui/mode-toggle';
|
|
1950
1729
|
import { Card, CardContent, CardHeader, CardTitle } from '@<%= projectName %>/ui/card';
|
|
1951
1730
|
|
|
@@ -2013,7 +1792,12 @@ export default function Home() {
|
|
|
2013
1792
|
{isPending ? (
|
|
2014
1793
|
<p className="text-sm text-muted-foreground">Loading\u2026</p>
|
|
2015
1794
|
) : session ? (
|
|
2016
|
-
<
|
|
1795
|
+
<div className="space-y-3">
|
|
1796
|
+
<p className="text-sm">Signed in as {session.user.name}</p>
|
|
1797
|
+
<Button variant="outline" onClick={() => authClient.signOut()}>
|
|
1798
|
+
Sign out
|
|
1799
|
+
</Button>
|
|
1800
|
+
</div>
|
|
2017
1801
|
) : (
|
|
2018
1802
|
<AuthForm />
|
|
2019
1803
|
)}
|
|
@@ -2185,169 +1969,22 @@ export function AuthForm() {
|
|
|
2185
1969
|
}`
|
|
2186
1970
|
};
|
|
2187
1971
|
|
|
2188
|
-
// src/shadcn-ui/templates/
|
|
2189
|
-
var
|
|
2190
|
-
filename: "
|
|
2191
|
-
template: `
|
|
1972
|
+
// src/shadcn-ui/templates/accordion.ts
|
|
1973
|
+
var accordion_default = {
|
|
1974
|
+
filename: "packages/ui/src/components/accordion.tsx",
|
|
1975
|
+
template: `import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
|
|
2192
1976
|
|
|
2193
|
-
import {
|
|
2194
|
-
import {
|
|
2195
|
-
import { authClient } from '@/lib/auth-client';
|
|
2196
|
-
import { Button } from '@<%= projectName %>/ui/button';
|
|
2197
|
-
import { Input } from '@<%= projectName %>/ui/input';
|
|
2198
|
-
import { Checkbox } from '@<%= projectName %>/ui/checkbox';
|
|
1977
|
+
import { cn } from "@<%= projectName %>/ui/lib/utils"
|
|
1978
|
+
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
2199
1979
|
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
createTodo: (title: string): Promise<Todo> =>
|
|
2209
|
-
fetch('/api/todos', {
|
|
2210
|
-
method: 'POST',
|
|
2211
|
-
headers: { 'Content-Type': 'application/json' },
|
|
2212
|
-
body: JSON.stringify({ title }),
|
|
2213
|
-
}).then((r) => r.json()),
|
|
2214
|
-
toggleTodo: (id: number): Promise<Todo> =>
|
|
2215
|
-
fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
|
|
2216
|
-
deleteTodo: (id: number): Promise<void> =>
|
|
2217
|
-
fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
|
|
2218
|
-
};
|
|
2219
|
-
|
|
2220
|
-
export function TodoList({
|
|
2221
|
-
session,
|
|
2222
|
-
}: {
|
|
2223
|
-
session: { user: { email: string } };
|
|
2224
|
-
}) {
|
|
2225
|
-
const queryClient = useQueryClient();
|
|
2226
|
-
|
|
2227
|
-
const { data: todos = [], isPending } = useQuery({
|
|
2228
|
-
queryKey: ['todos'],
|
|
2229
|
-
queryFn: api.getTodos,
|
|
2230
|
-
});
|
|
2231
|
-
|
|
2232
|
-
const createMutation = useMutation({
|
|
2233
|
-
mutationFn: api.createTodo,
|
|
2234
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
2235
|
-
});
|
|
2236
|
-
|
|
2237
|
-
const toggleMutation = useMutation({
|
|
2238
|
-
mutationFn: api.toggleTodo,
|
|
2239
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
2240
|
-
});
|
|
2241
|
-
|
|
2242
|
-
const deleteMutation = useMutation({
|
|
2243
|
-
mutationFn: api.deleteTodo,
|
|
2244
|
-
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
2245
|
-
});
|
|
2246
|
-
|
|
2247
|
-
const form = useForm({
|
|
2248
|
-
defaultValues: { title: '' },
|
|
2249
|
-
onSubmit: async ({ value }) => {
|
|
2250
|
-
if (!value.title.trim()) return;
|
|
2251
|
-
createMutation.mutate(value.title.trim());
|
|
2252
|
-
form.reset();
|
|
2253
|
-
},
|
|
2254
|
-
});
|
|
2255
|
-
|
|
2256
|
-
return (
|
|
2257
|
-
<>
|
|
2258
|
-
<div className="flex items-center justify-between mb-6">
|
|
2259
|
-
<h1 className="text-2xl font-semibold">Todos</h1>
|
|
2260
|
-
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
|
2261
|
-
<span>{session.user.email}</span>
|
|
2262
|
-
<Button
|
|
2263
|
-
variant="ghost"
|
|
2264
|
-
size="sm"
|
|
2265
|
-
onClick={() => authClient.signOut()}
|
|
2266
|
-
>
|
|
2267
|
-
Sign out
|
|
2268
|
-
</Button>
|
|
2269
|
-
</div>
|
|
2270
|
-
</div>
|
|
2271
|
-
|
|
2272
|
-
<form
|
|
2273
|
-
onSubmit={(e) => {
|
|
2274
|
-
e.preventDefault();
|
|
2275
|
-
form.handleSubmit();
|
|
2276
|
-
}}
|
|
2277
|
-
className="flex gap-2 mb-6"
|
|
2278
|
-
>
|
|
2279
|
-
<form.Field name="title">
|
|
2280
|
-
{(field) => (
|
|
2281
|
-
<Input
|
|
2282
|
-
value={field.state.value}
|
|
2283
|
-
onChange={(e) => field.handleChange(e.target.value)}
|
|
2284
|
-
placeholder="New todo\u2026"
|
|
2285
|
-
className="flex-1"
|
|
2286
|
-
/>
|
|
2287
|
-
)}
|
|
2288
|
-
</form.Field>
|
|
2289
|
-
|
|
2290
|
-
<form.Subscribe selector={(s) => s.isSubmitting}>
|
|
2291
|
-
{(isSubmitting) => (
|
|
2292
|
-
<Button type="submit" disabled={isSubmitting || createMutation.isPending}>
|
|
2293
|
-
Add
|
|
2294
|
-
</Button>
|
|
2295
|
-
)}
|
|
2296
|
-
</form.Subscribe>
|
|
2297
|
-
</form>
|
|
2298
|
-
|
|
2299
|
-
{isPending ? (
|
|
2300
|
-
<p className="text-sm text-muted-foreground">Loading\u2026</p>
|
|
2301
|
-
) : (
|
|
2302
|
-
<ul className="divide-y divide-border">
|
|
2303
|
-
{todos.map((todo) => (
|
|
2304
|
-
<li key={todo.id} className="flex items-center gap-3 py-3">
|
|
2305
|
-
<Checkbox
|
|
2306
|
-
checked={todo.done}
|
|
2307
|
-
onCheckedChange={() => toggleMutation.mutate(todo.id)}
|
|
2308
|
-
/>
|
|
2309
|
-
<span
|
|
2310
|
-
className={\`flex-1 text-sm \${todo.done ? 'line-through text-muted-foreground' : ''}\`}
|
|
2311
|
-
>
|
|
2312
|
-
{todo.title}
|
|
2313
|
-
</span>
|
|
2314
|
-
<Button
|
|
2315
|
-
variant="ghost"
|
|
2316
|
-
size="sm"
|
|
2317
|
-
onClick={() => deleteMutation.mutate(todo.id)}
|
|
2318
|
-
className="text-muted-foreground hover:text-destructive h-7 w-7 p-0"
|
|
2319
|
-
>
|
|
2320
|
-
\xD7
|
|
2321
|
-
</Button>
|
|
2322
|
-
</li>
|
|
2323
|
-
))}
|
|
2324
|
-
</ul>
|
|
2325
|
-
)}
|
|
2326
|
-
|
|
2327
|
-
{!isPending && todos.length === 0 && (
|
|
2328
|
-
<p className="text-sm text-muted-foreground">No todos yet.</p>
|
|
2329
|
-
)}
|
|
2330
|
-
</>
|
|
2331
|
-
);
|
|
2332
|
-
}`
|
|
2333
|
-
};
|
|
2334
|
-
|
|
2335
|
-
// src/shadcn-ui/templates/accordion.ts
|
|
2336
|
-
var accordion_default = {
|
|
2337
|
-
filename: "packages/ui/src/components/accordion.tsx",
|
|
2338
|
-
template: `import { Accordion as AccordionPrimitive } from "@base-ui/react/accordion"
|
|
2339
|
-
|
|
2340
|
-
import { cn } from "@<%= projectName %>/ui/lib/utils"
|
|
2341
|
-
import { ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
|
2342
|
-
|
|
2343
|
-
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
|
2344
|
-
return (
|
|
2345
|
-
<AccordionPrimitive.Root
|
|
2346
|
-
data-slot="accordion"
|
|
2347
|
-
className={cn("flex w-full flex-col", className)}
|
|
2348
|
-
{...props}
|
|
2349
|
-
/>
|
|
2350
|
-
)
|
|
1980
|
+
function Accordion({ className, ...props }: AccordionPrimitive.Root.Props) {
|
|
1981
|
+
return (
|
|
1982
|
+
<AccordionPrimitive.Root
|
|
1983
|
+
data-slot="accordion"
|
|
1984
|
+
className={cn("flex w-full flex-col", className)}
|
|
1985
|
+
{...props}
|
|
1986
|
+
/>
|
|
1987
|
+
)
|
|
2351
1988
|
}
|
|
2352
1989
|
|
|
2353
1990
|
function AccordionItem({ className, ...props }: AccordionPrimitive.Item.Props) {
|
|
@@ -9333,7 +8970,6 @@ var shadcnUi = {
|
|
|
9333
8970
|
page_default2,
|
|
9334
8971
|
providers_default2,
|
|
9335
8972
|
auth_form_default2,
|
|
9336
|
-
todo_list_default2,
|
|
9337
8973
|
accordion_default,
|
|
9338
8974
|
alert_dialog_default,
|
|
9339
8975
|
alert_default,
|
|
@@ -10444,54 +10080,6 @@ export async function inject<T>(token: Type<T> | Abstract<T> | string | symbol):
|
|
|
10444
10080
|
}`
|
|
10445
10081
|
};
|
|
10446
10082
|
|
|
10447
|
-
// src/nest-di-only/templates/web-todos-route.ts
|
|
10448
|
-
var web_todos_route_default = {
|
|
10449
|
-
filename: "apps/web/app/api/todos/route.ts",
|
|
10450
|
-
template: `import { NextResponse } from 'next/server';
|
|
10451
|
-
import { inject } from '@/lib/nest';
|
|
10452
|
-
import { TodosService } from '@<%= projectName %>/api';
|
|
10453
|
-
|
|
10454
|
-
export async function GET() {
|
|
10455
|
-
const todos = await inject(TodosService);
|
|
10456
|
-
return NextResponse.json(todos.findAll());
|
|
10457
|
-
}
|
|
10458
|
-
|
|
10459
|
-
export async function POST(req: Request) {
|
|
10460
|
-
const { title } = await req.json();
|
|
10461
|
-
const todos = await inject(TodosService);
|
|
10462
|
-
return NextResponse.json(todos.create(title), { status: 201 });
|
|
10463
|
-
}`
|
|
10464
|
-
};
|
|
10465
|
-
|
|
10466
|
-
// src/nest-di-only/templates/web-todos-id-route.ts
|
|
10467
|
-
var web_todos_id_route_default = {
|
|
10468
|
-
filename: "apps/web/app/api/todos/[id]/route.ts",
|
|
10469
|
-
template: `import { NextResponse } from 'next/server';
|
|
10470
|
-
import { inject } from '@/lib/nest';
|
|
10471
|
-
import { TodosService } from '@<%= projectName %>/api';
|
|
10472
|
-
|
|
10473
|
-
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10474
|
-
const { id } = await params;
|
|
10475
|
-
const todos = await inject(TodosService);
|
|
10476
|
-
todos.remove(Number(id));
|
|
10477
|
-
return new NextResponse(null, { status: 204 });
|
|
10478
|
-
}`
|
|
10479
|
-
};
|
|
10480
|
-
|
|
10481
|
-
// src/nest-di-only/templates/web-todos-toggle-route.ts
|
|
10482
|
-
var web_todos_toggle_route_default = {
|
|
10483
|
-
filename: "apps/web/app/api/todos/[id]/toggle/route.ts",
|
|
10484
|
-
template: `import { NextResponse } from 'next/server';
|
|
10485
|
-
import { inject } from '@/lib/nest';
|
|
10486
|
-
import { TodosService } from '@<%= projectName %>/api';
|
|
10487
|
-
|
|
10488
|
-
export async function PATCH(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10489
|
-
const { id } = await params;
|
|
10490
|
-
const todos = await inject(TodosService);
|
|
10491
|
-
return NextResponse.json(todos.toggle(Number(id)));
|
|
10492
|
-
}`
|
|
10493
|
-
};
|
|
10494
|
-
|
|
10495
10083
|
// src/nest-di-only/templates/web-package-json.ts
|
|
10496
10084
|
var web_package_json_default2 = {
|
|
10497
10085
|
filename: "apps/web/package.json",
|
|
@@ -10613,9 +10201,6 @@ var nestDiOnly = {
|
|
|
10613
10201
|
api_index_default,
|
|
10614
10202
|
web_nest_context_default,
|
|
10615
10203
|
web_hello_route_default,
|
|
10616
|
-
web_todos_route_default,
|
|
10617
|
-
web_todos_id_route_default,
|
|
10618
|
-
web_todos_toggle_route_default,
|
|
10619
10204
|
web_package_json_default2,
|
|
10620
10205
|
web_next_config_default3,
|
|
10621
10206
|
web_tsconfig_default
|
|
@@ -10682,10 +10267,9 @@ import { AuthGuard, AuthModule } from '@thallesp/nestjs-better-auth';
|
|
|
10682
10267
|
import { auth } from '@<%= projectName %>/auth';
|
|
10683
10268
|
import { AppController } from './app.controller';
|
|
10684
10269
|
import { AppService } from './app.service';
|
|
10685
|
-
import { TodosModule } from './todos/todos.module';
|
|
10686
10270
|
|
|
10687
10271
|
@Module({
|
|
10688
|
-
imports: [AuthModule.forRoot({ auth })
|
|
10272
|
+
imports: [AuthModule.forRoot({ auth })],
|
|
10689
10273
|
controllers: [AppController],
|
|
10690
10274
|
providers: [AppService, { provide: APP_GUARD, useClass: AuthGuard }],
|
|
10691
10275
|
})
|
|
@@ -10717,72 +10301,720 @@ async function bootstrap() {
|
|
|
10717
10301
|
void bootstrap();`
|
|
10718
10302
|
};
|
|
10719
10303
|
|
|
10720
|
-
// src/api-controllers/
|
|
10721
|
-
var
|
|
10722
|
-
|
|
10723
|
-
|
|
10724
|
-
|
|
10725
|
-
|
|
10726
|
-
|
|
10727
|
-
|
|
10728
|
-
Param,
|
|
10729
|
-
Patch,
|
|
10730
|
-
Post,
|
|
10731
|
-
} from '@nestjs/common';
|
|
10732
|
-
import { TodosService } from './todos.service';
|
|
10733
|
-
|
|
10734
|
-
@Controller('todos')
|
|
10735
|
-
export class TodosController {
|
|
10736
|
-
constructor(private readonly todosService: TodosService) {}
|
|
10737
|
-
|
|
10738
|
-
@Get()
|
|
10739
|
-
findAll() {
|
|
10740
|
-
return this.todosService.findAll();
|
|
10741
|
-
}
|
|
10742
|
-
|
|
10743
|
-
@Post()
|
|
10744
|
-
create(@Body('title') title: string) {
|
|
10745
|
-
return this.todosService.create(title);
|
|
10746
|
-
}
|
|
10747
|
-
|
|
10748
|
-
@Patch(':id/toggle')
|
|
10749
|
-
toggle(@Param('id') id: string) {
|
|
10750
|
-
return this.todosService.toggle(Number(id));
|
|
10751
|
-
}
|
|
10752
|
-
|
|
10753
|
-
@Delete(':id')
|
|
10754
|
-
remove(@Param('id') id: string) {
|
|
10755
|
-
return this.todosService.remove(Number(id));
|
|
10756
|
-
}
|
|
10757
|
-
}`
|
|
10304
|
+
// src/api-controllers/index.ts
|
|
10305
|
+
var apiControllers = {
|
|
10306
|
+
templates: [
|
|
10307
|
+
app_controller_default,
|
|
10308
|
+
app_controller_spec_default,
|
|
10309
|
+
app_module_default2,
|
|
10310
|
+
main_default
|
|
10311
|
+
]
|
|
10758
10312
|
};
|
|
10313
|
+
var api_controllers_default = apiControllers;
|
|
10759
10314
|
|
|
10760
|
-
// src/
|
|
10761
|
-
var
|
|
10315
|
+
// src/todo-example/templates/todos-module.ts
|
|
10316
|
+
var todos_module_default = {
|
|
10762
10317
|
filename: "apps/api/src/todos/todos.module.ts",
|
|
10763
10318
|
template: `import { Module } from '@nestjs/common';
|
|
10764
|
-
import { TodosController } from './todos.controller';
|
|
10765
10319
|
import { TodosService } from './todos.service';
|
|
10766
10320
|
|
|
10767
10321
|
@Module({
|
|
10768
|
-
controllers: [TodosController],
|
|
10769
10322
|
providers: [TodosService],
|
|
10323
|
+
exports: [TodosService],
|
|
10770
10324
|
})
|
|
10771
10325
|
export class TodosModule {}`
|
|
10772
10326
|
};
|
|
10773
10327
|
|
|
10774
|
-
// src/
|
|
10775
|
-
var
|
|
10776
|
-
|
|
10777
|
-
|
|
10778
|
-
|
|
10779
|
-
|
|
10780
|
-
|
|
10781
|
-
|
|
10782
|
-
|
|
10783
|
-
|
|
10784
|
-
|
|
10785
|
-
|
|
10328
|
+
// src/todo-example/templates/todos-service.ts
|
|
10329
|
+
var todos_service_default = {
|
|
10330
|
+
filename: "apps/api/src/todos/todos.service.ts",
|
|
10331
|
+
template: `import { Injectable, NotFoundException } from '@nestjs/common';
|
|
10332
|
+
|
|
10333
|
+
export interface Todo {
|
|
10334
|
+
id: number;
|
|
10335
|
+
title: string;
|
|
10336
|
+
done: boolean;
|
|
10337
|
+
}
|
|
10338
|
+
|
|
10339
|
+
@Injectable()
|
|
10340
|
+
export class TodosService {
|
|
10341
|
+
private todos: Todo[] = [];
|
|
10342
|
+
private nextId = 1;
|
|
10343
|
+
|
|
10344
|
+
findAll(): Todo[] {
|
|
10345
|
+
return this.todos;
|
|
10346
|
+
}
|
|
10347
|
+
|
|
10348
|
+
create(title: string): Todo {
|
|
10349
|
+
const todo: Todo = { id: this.nextId++, title, done: false };
|
|
10350
|
+
this.todos.push(todo);
|
|
10351
|
+
return todo;
|
|
10352
|
+
}
|
|
10353
|
+
|
|
10354
|
+
toggle(id: number): Todo {
|
|
10355
|
+
const todo = this.todos.find((t) => t.id === id);
|
|
10356
|
+
if (!todo) throw new NotFoundException(\`Todo \${id} not found\`);
|
|
10357
|
+
todo.done = !todo.done;
|
|
10358
|
+
return todo;
|
|
10359
|
+
}
|
|
10360
|
+
|
|
10361
|
+
remove(id: number): void {
|
|
10362
|
+
const index = this.todos.findIndex((t) => t.id === id);
|
|
10363
|
+
if (index === -1) throw new NotFoundException(\`Todo \${id} not found\`);
|
|
10364
|
+
this.todos.splice(index, 1);
|
|
10365
|
+
}
|
|
10366
|
+
}`
|
|
10367
|
+
};
|
|
10368
|
+
|
|
10369
|
+
// src/todo-example/templates/todos-service-spec.ts
|
|
10370
|
+
var todos_service_spec_default = {
|
|
10371
|
+
filename: "apps/api/src/todos/todos.service.spec.ts",
|
|
10372
|
+
template: `import { Test, TestingModule } from '@nestjs/testing';
|
|
10373
|
+
import { TodosService } from './todos.service';
|
|
10374
|
+
|
|
10375
|
+
describe('TodosService', () => {
|
|
10376
|
+
let service: TodosService;
|
|
10377
|
+
|
|
10378
|
+
beforeEach(async () => {
|
|
10379
|
+
const module: TestingModule = await Test.createTestingModule({
|
|
10380
|
+
providers: [TodosService],
|
|
10381
|
+
}).compile();
|
|
10382
|
+
|
|
10383
|
+
service = module.get<TodosService>(TodosService);
|
|
10384
|
+
});
|
|
10385
|
+
|
|
10386
|
+
it('should be defined', () => {
|
|
10387
|
+
expect(service).toBeDefined();
|
|
10388
|
+
});
|
|
10389
|
+
});`
|
|
10390
|
+
};
|
|
10391
|
+
|
|
10392
|
+
// src/todo-example/templates/todos-controller.ts
|
|
10393
|
+
var todos_controller_default = {
|
|
10394
|
+
filename: "apps/api/src/todos/todos.controller.ts",
|
|
10395
|
+
template: `import {
|
|
10396
|
+
Body,
|
|
10397
|
+
Controller,
|
|
10398
|
+
Delete,
|
|
10399
|
+
Get,
|
|
10400
|
+
Param,
|
|
10401
|
+
Patch,
|
|
10402
|
+
Post,
|
|
10403
|
+
} from '@nestjs/common';
|
|
10404
|
+
import { TodosService } from './todos.service';
|
|
10405
|
+
|
|
10406
|
+
@Controller('todos')
|
|
10407
|
+
export class TodosController {
|
|
10408
|
+
constructor(private readonly todosService: TodosService) {}
|
|
10409
|
+
|
|
10410
|
+
@Get()
|
|
10411
|
+
findAll() {
|
|
10412
|
+
return this.todosService.findAll();
|
|
10413
|
+
}
|
|
10414
|
+
|
|
10415
|
+
@Post()
|
|
10416
|
+
create(@Body('title') title: string) {
|
|
10417
|
+
return this.todosService.create(title);
|
|
10418
|
+
}
|
|
10419
|
+
|
|
10420
|
+
@Patch(':id/toggle')
|
|
10421
|
+
toggle(@Param('id') id: string) {
|
|
10422
|
+
return this.todosService.toggle(Number(id));
|
|
10423
|
+
}
|
|
10424
|
+
|
|
10425
|
+
@Delete(':id')
|
|
10426
|
+
remove(@Param('id') id: string) {
|
|
10427
|
+
return this.todosService.remove(Number(id));
|
|
10428
|
+
}
|
|
10429
|
+
}`
|
|
10430
|
+
};
|
|
10431
|
+
|
|
10432
|
+
// src/todo-example/templates/todos-module-controllers.ts
|
|
10433
|
+
var todos_module_controllers_default = {
|
|
10434
|
+
filename: "apps/api/src/todos/todos.module.ts",
|
|
10435
|
+
template: `import { Module } from '@nestjs/common';
|
|
10436
|
+
import { TodosController } from './todos.controller';
|
|
10437
|
+
import { TodosService } from './todos.service';
|
|
10438
|
+
|
|
10439
|
+
@Module({
|
|
10440
|
+
controllers: [TodosController],
|
|
10441
|
+
providers: [TodosService],
|
|
10442
|
+
})
|
|
10443
|
+
export class TodosModule {}`
|
|
10444
|
+
};
|
|
10445
|
+
|
|
10446
|
+
// src/todo-example/templates/app-module-controllers.ts
|
|
10447
|
+
var app_module_controllers_default = {
|
|
10448
|
+
filename: "apps/api/src/app.module.ts",
|
|
10449
|
+
template: `import { Module } from '@nestjs/common';
|
|
10450
|
+
import { APP_GUARD } from '@nestjs/core';
|
|
10451
|
+
import { AuthGuard, AuthModule } from '@thallesp/nestjs-better-auth';
|
|
10452
|
+
import { auth } from '@<%= projectName %>/auth';
|
|
10453
|
+
import { AppController } from './app.controller';
|
|
10454
|
+
import { AppService } from './app.service';
|
|
10455
|
+
import { TodosModule } from './todos/todos.module';
|
|
10456
|
+
|
|
10457
|
+
@Module({
|
|
10458
|
+
imports: [AuthModule.forRoot({ auth }), TodosModule],
|
|
10459
|
+
controllers: [AppController],
|
|
10460
|
+
providers: [AppService, { provide: APP_GUARD, useClass: AuthGuard }],
|
|
10461
|
+
})
|
|
10462
|
+
export class AppModule {}`
|
|
10463
|
+
};
|
|
10464
|
+
|
|
10465
|
+
// src/todo-example/templates/app-module-di.ts
|
|
10466
|
+
var app_module_di_default = {
|
|
10467
|
+
filename: "apps/api/src/app.module.ts",
|
|
10468
|
+
template: `import { Module } from '@nestjs/common';
|
|
10469
|
+
import { AppService } from './app.service';
|
|
10470
|
+
import { TodosModule } from './todos/todos.module';
|
|
10471
|
+
|
|
10472
|
+
@Module({
|
|
10473
|
+
imports: [TodosModule],
|
|
10474
|
+
providers: [AppService],
|
|
10475
|
+
})
|
|
10476
|
+
export class AppModule {}`
|
|
10477
|
+
};
|
|
10478
|
+
|
|
10479
|
+
// src/todo-example/templates/web-todos-route.ts
|
|
10480
|
+
var web_todos_route_default = {
|
|
10481
|
+
filename: "apps/web/app/api/todos/route.ts",
|
|
10482
|
+
template: `import { NextResponse } from 'next/server';
|
|
10483
|
+
import { inject } from '@/lib/nest';
|
|
10484
|
+
import { TodosService } from '@<%= projectName %>/api';
|
|
10485
|
+
|
|
10486
|
+
export async function GET() {
|
|
10487
|
+
const todos = await inject(TodosService);
|
|
10488
|
+
return NextResponse.json(todos.findAll());
|
|
10489
|
+
}
|
|
10490
|
+
|
|
10491
|
+
export async function POST(req: Request) {
|
|
10492
|
+
const { title } = await req.json();
|
|
10493
|
+
const todos = await inject(TodosService);
|
|
10494
|
+
return NextResponse.json(todos.create(title), { status: 201 });
|
|
10495
|
+
}`
|
|
10496
|
+
};
|
|
10497
|
+
|
|
10498
|
+
// src/todo-example/templates/web-todos-id-route.ts
|
|
10499
|
+
var web_todos_id_route_default = {
|
|
10500
|
+
filename: "apps/web/app/api/todos/[id]/route.ts",
|
|
10501
|
+
template: `import { NextResponse } from 'next/server';
|
|
10502
|
+
import { inject } from '@/lib/nest';
|
|
10503
|
+
import { TodosService } from '@<%= projectName %>/api';
|
|
10504
|
+
|
|
10505
|
+
export async function DELETE(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10506
|
+
const { id } = await params;
|
|
10507
|
+
const todos = await inject(TodosService);
|
|
10508
|
+
todos.remove(Number(id));
|
|
10509
|
+
return new NextResponse(null, { status: 204 });
|
|
10510
|
+
}`
|
|
10511
|
+
};
|
|
10512
|
+
|
|
10513
|
+
// src/todo-example/templates/web-todos-toggle-route.ts
|
|
10514
|
+
var web_todos_toggle_route_default = {
|
|
10515
|
+
filename: "apps/web/app/api/todos/[id]/toggle/route.ts",
|
|
10516
|
+
template: `import { NextResponse } from 'next/server';
|
|
10517
|
+
import { inject } from '@/lib/nest';
|
|
10518
|
+
import { TodosService } from '@<%= projectName %>/api';
|
|
10519
|
+
|
|
10520
|
+
export async function PATCH(_req: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
10521
|
+
const { id } = await params;
|
|
10522
|
+
const todos = await inject(TodosService);
|
|
10523
|
+
return NextResponse.json(todos.toggle(Number(id)));
|
|
10524
|
+
}`
|
|
10525
|
+
};
|
|
10526
|
+
|
|
10527
|
+
// src/todo-example/templates/web-todo-list.ts
|
|
10528
|
+
var web_todo_list_default = {
|
|
10529
|
+
filename: "apps/web/app/todo-list.tsx",
|
|
10530
|
+
template: `'use client';
|
|
10531
|
+
|
|
10532
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
10533
|
+
import { useForm } from '@tanstack/react-form';
|
|
10534
|
+
import { authClient } from '@/lib/auth-client';
|
|
10535
|
+
import { Button } from '@<%= projectName %>/ui/button';
|
|
10536
|
+
|
|
10537
|
+
interface Todo {
|
|
10538
|
+
id: number;
|
|
10539
|
+
title: string;
|
|
10540
|
+
done: boolean;
|
|
10541
|
+
}
|
|
10542
|
+
|
|
10543
|
+
const api = {
|
|
10544
|
+
getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
|
|
10545
|
+
createTodo: (title: string): Promise<Todo> =>
|
|
10546
|
+
fetch('/api/todos', {
|
|
10547
|
+
method: 'POST',
|
|
10548
|
+
headers: { 'Content-Type': 'application/json' },
|
|
10549
|
+
body: JSON.stringify({ title }),
|
|
10550
|
+
}).then((r) => r.json()),
|
|
10551
|
+
toggleTodo: (id: number): Promise<Todo> =>
|
|
10552
|
+
fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
|
|
10553
|
+
deleteTodo: (id: number): Promise<void> =>
|
|
10554
|
+
fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
|
|
10555
|
+
};
|
|
10556
|
+
|
|
10557
|
+
export function TodoList({
|
|
10558
|
+
session,
|
|
10559
|
+
}: {
|
|
10560
|
+
session: { user: { email: string } };
|
|
10561
|
+
}) {
|
|
10562
|
+
const queryClient = useQueryClient();
|
|
10563
|
+
|
|
10564
|
+
const { data: todos = [], isPending } = useQuery({
|
|
10565
|
+
queryKey: ['todos'],
|
|
10566
|
+
queryFn: api.getTodos,
|
|
10567
|
+
});
|
|
10568
|
+
|
|
10569
|
+
const createMutation = useMutation({
|
|
10570
|
+
mutationFn: api.createTodo,
|
|
10571
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
10572
|
+
});
|
|
10573
|
+
|
|
10574
|
+
const toggleMutation = useMutation({
|
|
10575
|
+
mutationFn: api.toggleTodo,
|
|
10576
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
10577
|
+
});
|
|
10578
|
+
|
|
10579
|
+
const deleteMutation = useMutation({
|
|
10580
|
+
mutationFn: api.deleteTodo,
|
|
10581
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
10582
|
+
});
|
|
10583
|
+
|
|
10584
|
+
const form = useForm({
|
|
10585
|
+
defaultValues: { title: '' },
|
|
10586
|
+
onSubmit: async ({ value }) => {
|
|
10587
|
+
if (!value.title.trim()) return;
|
|
10588
|
+
createMutation.mutate(value.title.trim());
|
|
10589
|
+
form.reset();
|
|
10590
|
+
},
|
|
10591
|
+
});
|
|
10592
|
+
|
|
10593
|
+
return (
|
|
10594
|
+
<>
|
|
10595
|
+
<div className="flex items-center justify-between mb-8">
|
|
10596
|
+
<h1 className="text-2xl font-semibold">Todos</h1>
|
|
10597
|
+
<div className="flex items-center gap-3 text-sm text-gray-400">
|
|
10598
|
+
<span>{session.user.email}</span>
|
|
10599
|
+
<button
|
|
10600
|
+
className="hover:text-gray-100"
|
|
10601
|
+
onClick={() => authClient.signOut()}
|
|
10602
|
+
>
|
|
10603
|
+
Sign out
|
|
10604
|
+
</button>
|
|
10605
|
+
</div>
|
|
10606
|
+
</div>
|
|
10607
|
+
|
|
10608
|
+
<form
|
|
10609
|
+
onSubmit={(e) => {
|
|
10610
|
+
e.preventDefault();
|
|
10611
|
+
form.handleSubmit();
|
|
10612
|
+
}}
|
|
10613
|
+
className="flex gap-2 mb-6"
|
|
10614
|
+
>
|
|
10615
|
+
<form.Field name="title">
|
|
10616
|
+
{(field) => (
|
|
10617
|
+
<input
|
|
10618
|
+
value={field.state.value}
|
|
10619
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
10620
|
+
placeholder="New todo\u2026"
|
|
10621
|
+
className="flex h-9 flex-1 rounded-md border border-neutral-700 bg-neutral-800/50 px-3 py-1 text-sm shadow-xs transition-[color,box-shadow] outline-none placeholder:text-neutral-500 focus-visible:border-neutral-500 focus-visible:ring-2 focus-visible:ring-neutral-500/20"
|
|
10622
|
+
/>
|
|
10623
|
+
)}
|
|
10624
|
+
</form.Field>
|
|
10625
|
+
|
|
10626
|
+
<form.Subscribe selector={(s) => s.isSubmitting}>
|
|
10627
|
+
{(isSubmitting) => (
|
|
10628
|
+
<Button type="submit" disabled={isSubmitting || createMutation.isPending}>
|
|
10629
|
+
Add
|
|
10630
|
+
</Button>
|
|
10631
|
+
)}
|
|
10632
|
+
</form.Subscribe>
|
|
10633
|
+
</form>
|
|
10634
|
+
|
|
10635
|
+
{isPending ? (
|
|
10636
|
+
<p className="text-sm text-gray-400">Loading\u2026</p>
|
|
10637
|
+
) : (
|
|
10638
|
+
<ul className="divide-y divide-neutral-700">
|
|
10639
|
+
{todos.map((todo) => (
|
|
10640
|
+
<li key={todo.id} className="flex items-center gap-3 py-3">
|
|
10641
|
+
<input
|
|
10642
|
+
type="checkbox"
|
|
10643
|
+
checked={todo.done}
|
|
10644
|
+
onChange={() => toggleMutation.mutate(todo.id)}
|
|
10645
|
+
/>
|
|
10646
|
+
<span
|
|
10647
|
+
className={\`flex-1 text-sm \${todo.done ? 'line-through text-gray-400' : ''}\`}
|
|
10648
|
+
>
|
|
10649
|
+
{todo.title}
|
|
10650
|
+
</span>
|
|
10651
|
+
<button
|
|
10652
|
+
onClick={() => deleteMutation.mutate(todo.id)}
|
|
10653
|
+
className="text-gray-600 hover:text-red-400 text-lg leading-none"
|
|
10654
|
+
>
|
|
10655
|
+
\xD7
|
|
10656
|
+
</button>
|
|
10657
|
+
</li>
|
|
10658
|
+
))}
|
|
10659
|
+
</ul>
|
|
10660
|
+
)}
|
|
10661
|
+
|
|
10662
|
+
{!isPending && todos.length === 0 && (
|
|
10663
|
+
<p className="text-sm text-gray-400">No todos yet.</p>
|
|
10664
|
+
)}
|
|
10665
|
+
</>
|
|
10666
|
+
);
|
|
10667
|
+
}`
|
|
10668
|
+
};
|
|
10669
|
+
|
|
10670
|
+
// src/todo-example/templates/web-page.ts
|
|
10671
|
+
var web_page_default = {
|
|
10672
|
+
filename: "apps/web/app/page.tsx",
|
|
10673
|
+
template: `'use client';
|
|
10674
|
+
|
|
10675
|
+
import { useQuery } from '@tanstack/react-query';
|
|
10676
|
+
import { authClient } from '@/lib/auth-client';
|
|
10677
|
+
import { AuthForm } from '@/app/auth-form';
|
|
10678
|
+
import { Link } from '@<%= projectName %>/ui/link';
|
|
10679
|
+
import { Logo } from '@<%= projectName %>/ui/logo';
|
|
10680
|
+
import { TodoList } from '@/app/todo-list';
|
|
10681
|
+
|
|
10682
|
+
export default function Home() {
|
|
10683
|
+
const { data: session, isPending } = authClient.useSession();
|
|
10684
|
+
|
|
10685
|
+
const { data: hello } = useQuery({
|
|
10686
|
+
queryKey: ['hello'],
|
|
10687
|
+
queryFn: () => fetch('/api/hello').then((r) => r.json()),
|
|
10688
|
+
});
|
|
10689
|
+
|
|
10690
|
+
return (
|
|
10691
|
+
<main className="max-w-lg mx-auto min-h-full px-4 py-8 space-y-4">
|
|
10692
|
+
<div className="pb-4 border-b">
|
|
10693
|
+
<p className="font-mono text-sm text-gray-400">apps/web/page.tsx</p>
|
|
10694
|
+
<p className="text-sm text-gray-400">Delete me to get started!</p>
|
|
10695
|
+
</div>
|
|
10696
|
+
|
|
10697
|
+
<div className="flex items-center gap-3 py-2">
|
|
10698
|
+
<Logo className="w-10 h-auto text-foreground" />
|
|
10699
|
+
<div>
|
|
10700
|
+
<h1 className="text-4xl font-black tracking-tight"><%= projectName %></h1>
|
|
10701
|
+
<p className="text-sm text-gray-400 tracking-widest uppercase">
|
|
10702
|
+
Next + Nest = Newt \u{1F49C}
|
|
10703
|
+
</p>
|
|
10704
|
+
</div>
|
|
10705
|
+
</div>
|
|
10706
|
+
|
|
10707
|
+
<div className="rounded-xl border p-6 space-y-1">
|
|
10708
|
+
<p className="text-xs font-mono uppercase tracking-widest text-gray-400">next.js</p>
|
|
10709
|
+
<p className="font-mono text-sm text-gray-400">apps/web/layout.tsx</p>
|
|
10710
|
+
<p className="text-sm text-gray-400">Next.js rendering</p>
|
|
10711
|
+
</div>
|
|
10712
|
+
|
|
10713
|
+
<div className="rounded-xl border p-6 space-y-1">
|
|
10714
|
+
<p className="text-xs font-mono uppercase tracking-widest text-gray-400">nest.js</p>
|
|
10715
|
+
<p className="font-mono text-sm text-gray-400">GET /api/hello</p>
|
|
10716
|
+
<pre className="mt-2 border rounded-md p-3 text-sm bg-neutral-800">
|
|
10717
|
+
<code>{JSON.stringify(hello, null, 2)}</code>
|
|
10718
|
+
</pre>
|
|
10719
|
+
</div>
|
|
10720
|
+
|
|
10721
|
+
<div className="rounded-xl border p-6">
|
|
10722
|
+
<p className="text-xs font-mono uppercase tracking-widest text-gray-400 mb-4">better-auth</p>
|
|
10723
|
+
{isPending ? (
|
|
10724
|
+
<p className="text-sm text-gray-400">Loading\u2026</p>
|
|
10725
|
+
) : session ? (
|
|
10726
|
+
<TodoList session={session} />
|
|
10727
|
+
) : (
|
|
10728
|
+
<AuthForm />
|
|
10729
|
+
)}
|
|
10730
|
+
</div>
|
|
10731
|
+
|
|
10732
|
+
<div className="px-2 py-4 text-sm">
|
|
10733
|
+
<p className="mb-2 text-gray-400">Learn more</p>
|
|
10734
|
+
<ul className="list-disc list-inside space-y-2 mt-2">
|
|
10735
|
+
<li>
|
|
10736
|
+
<Link href="https://github.com">GitHub</Link>
|
|
10737
|
+
</li>
|
|
10738
|
+
<li>
|
|
10739
|
+
<Link href="https://nextjs.org">Next.js</Link>
|
|
10740
|
+
</li>
|
|
10741
|
+
<li>
|
|
10742
|
+
<Link href="https://nestjs.com">NestJS</Link>
|
|
10743
|
+
</li>
|
|
10744
|
+
</ul>
|
|
10745
|
+
</div>
|
|
10746
|
+
</main>
|
|
10747
|
+
);
|
|
10748
|
+
}`
|
|
10749
|
+
};
|
|
10750
|
+
|
|
10751
|
+
// src/todo-example/templates/shadcn-todo-list.ts
|
|
10752
|
+
var shadcn_todo_list_default = {
|
|
10753
|
+
filename: "apps/web/app/todo-list.tsx",
|
|
10754
|
+
template: `'use client';
|
|
10755
|
+
|
|
10756
|
+
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
10757
|
+
import { useForm } from '@tanstack/react-form';
|
|
10758
|
+
import { authClient } from '@/lib/auth-client';
|
|
10759
|
+
import { Button } from '@<%= projectName %>/ui/button';
|
|
10760
|
+
import { Input } from '@<%= projectName %>/ui/input';
|
|
10761
|
+
import { Checkbox } from '@<%= projectName %>/ui/checkbox';
|
|
10762
|
+
|
|
10763
|
+
interface Todo {
|
|
10764
|
+
id: number;
|
|
10765
|
+
title: string;
|
|
10766
|
+
done: boolean;
|
|
10767
|
+
}
|
|
10768
|
+
|
|
10769
|
+
const api = {
|
|
10770
|
+
getTodos: (): Promise<Todo[]> => fetch('/api/todos').then((r) => r.json()),
|
|
10771
|
+
createTodo: (title: string): Promise<Todo> =>
|
|
10772
|
+
fetch('/api/todos', {
|
|
10773
|
+
method: 'POST',
|
|
10774
|
+
headers: { 'Content-Type': 'application/json' },
|
|
10775
|
+
body: JSON.stringify({ title }),
|
|
10776
|
+
}).then((r) => r.json()),
|
|
10777
|
+
toggleTodo: (id: number): Promise<Todo> =>
|
|
10778
|
+
fetch(\`/api/todos/\${id}/toggle\`, { method: 'PATCH' }).then((r) => r.json()),
|
|
10779
|
+
deleteTodo: (id: number): Promise<void> =>
|
|
10780
|
+
fetch(\`/api/todos/\${id}\`, { method: 'DELETE' }).then(() => undefined),
|
|
10781
|
+
};
|
|
10782
|
+
|
|
10783
|
+
export function TodoList({
|
|
10784
|
+
session,
|
|
10785
|
+
}: {
|
|
10786
|
+
session: { user: { email: string } };
|
|
10787
|
+
}) {
|
|
10788
|
+
const queryClient = useQueryClient();
|
|
10789
|
+
|
|
10790
|
+
const { data: todos = [], isPending } = useQuery({
|
|
10791
|
+
queryKey: ['todos'],
|
|
10792
|
+
queryFn: api.getTodos,
|
|
10793
|
+
});
|
|
10794
|
+
|
|
10795
|
+
const createMutation = useMutation({
|
|
10796
|
+
mutationFn: api.createTodo,
|
|
10797
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
10798
|
+
});
|
|
10799
|
+
|
|
10800
|
+
const toggleMutation = useMutation({
|
|
10801
|
+
mutationFn: api.toggleTodo,
|
|
10802
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
10803
|
+
});
|
|
10804
|
+
|
|
10805
|
+
const deleteMutation = useMutation({
|
|
10806
|
+
mutationFn: api.deleteTodo,
|
|
10807
|
+
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['todos'] }),
|
|
10808
|
+
});
|
|
10809
|
+
|
|
10810
|
+
const form = useForm({
|
|
10811
|
+
defaultValues: { title: '' },
|
|
10812
|
+
onSubmit: async ({ value }) => {
|
|
10813
|
+
if (!value.title.trim()) return;
|
|
10814
|
+
createMutation.mutate(value.title.trim());
|
|
10815
|
+
form.reset();
|
|
10816
|
+
},
|
|
10817
|
+
});
|
|
10818
|
+
|
|
10819
|
+
return (
|
|
10820
|
+
<>
|
|
10821
|
+
<div className="flex items-center justify-between mb-6">
|
|
10822
|
+
<h1 className="text-2xl font-semibold">Todos</h1>
|
|
10823
|
+
<div className="flex items-center gap-3 text-sm text-muted-foreground">
|
|
10824
|
+
<span>{session.user.email}</span>
|
|
10825
|
+
<Button
|
|
10826
|
+
variant="ghost"
|
|
10827
|
+
size="sm"
|
|
10828
|
+
onClick={() => authClient.signOut()}
|
|
10829
|
+
>
|
|
10830
|
+
Sign out
|
|
10831
|
+
</Button>
|
|
10832
|
+
</div>
|
|
10833
|
+
</div>
|
|
10834
|
+
|
|
10835
|
+
<form
|
|
10836
|
+
onSubmit={(e) => {
|
|
10837
|
+
e.preventDefault();
|
|
10838
|
+
form.handleSubmit();
|
|
10839
|
+
}}
|
|
10840
|
+
className="flex gap-2 mb-6"
|
|
10841
|
+
>
|
|
10842
|
+
<form.Field name="title">
|
|
10843
|
+
{(field) => (
|
|
10844
|
+
<Input
|
|
10845
|
+
value={field.state.value}
|
|
10846
|
+
onChange={(e) => field.handleChange(e.target.value)}
|
|
10847
|
+
placeholder="New todo\u2026"
|
|
10848
|
+
className="flex-1"
|
|
10849
|
+
/>
|
|
10850
|
+
)}
|
|
10851
|
+
</form.Field>
|
|
10852
|
+
|
|
10853
|
+
<form.Subscribe selector={(s) => s.isSubmitting}>
|
|
10854
|
+
{(isSubmitting) => (
|
|
10855
|
+
<Button type="submit" disabled={isSubmitting || createMutation.isPending}>
|
|
10856
|
+
Add
|
|
10857
|
+
</Button>
|
|
10858
|
+
)}
|
|
10859
|
+
</form.Subscribe>
|
|
10860
|
+
</form>
|
|
10861
|
+
|
|
10862
|
+
{isPending ? (
|
|
10863
|
+
<p className="text-sm text-muted-foreground">Loading\u2026</p>
|
|
10864
|
+
) : (
|
|
10865
|
+
<ul className="divide-y divide-border">
|
|
10866
|
+
{todos.map((todo) => (
|
|
10867
|
+
<li key={todo.id} className="flex items-center gap-3 py-3">
|
|
10868
|
+
<Checkbox
|
|
10869
|
+
checked={todo.done}
|
|
10870
|
+
onCheckedChange={() => toggleMutation.mutate(todo.id)}
|
|
10871
|
+
/>
|
|
10872
|
+
<span
|
|
10873
|
+
className={\`flex-1 text-sm \${todo.done ? 'line-through text-muted-foreground' : ''}\`}
|
|
10874
|
+
>
|
|
10875
|
+
{todo.title}
|
|
10876
|
+
</span>
|
|
10877
|
+
<Button
|
|
10878
|
+
variant="ghost"
|
|
10879
|
+
size="sm"
|
|
10880
|
+
onClick={() => deleteMutation.mutate(todo.id)}
|
|
10881
|
+
className="text-muted-foreground hover:text-destructive h-7 w-7 p-0"
|
|
10882
|
+
>
|
|
10883
|
+
\xD7
|
|
10884
|
+
</Button>
|
|
10885
|
+
</li>
|
|
10886
|
+
))}
|
|
10887
|
+
</ul>
|
|
10888
|
+
)}
|
|
10889
|
+
|
|
10890
|
+
{!isPending && todos.length === 0 && (
|
|
10891
|
+
<p className="text-sm text-muted-foreground">No todos yet.</p>
|
|
10892
|
+
)}
|
|
10893
|
+
</>
|
|
10894
|
+
);
|
|
10895
|
+
}`
|
|
10896
|
+
};
|
|
10897
|
+
|
|
10898
|
+
// src/todo-example/templates/shadcn-page.ts
|
|
10899
|
+
var shadcn_page_default = {
|
|
10900
|
+
filename: "apps/web/app/page.tsx",
|
|
10901
|
+
template: `'use client';
|
|
10902
|
+
|
|
10903
|
+
import { useQuery } from '@tanstack/react-query';
|
|
10904
|
+
import { authClient } from '@/lib/auth-client';
|
|
10905
|
+
import { AuthForm } from '@/app/auth-form';
|
|
10906
|
+
import { Link } from '@<%= projectName %>/ui/link';
|
|
10907
|
+
import { Logo } from '@<%= projectName %>/ui/logo';
|
|
10908
|
+
import { TodoList } from '@/app/todo-list';
|
|
10909
|
+
import { ModeToggle } from '@<%= projectName %>/ui/mode-toggle';
|
|
10910
|
+
import { Card, CardContent, CardHeader, CardTitle } from '@<%= projectName %>/ui/card';
|
|
10911
|
+
|
|
10912
|
+
export default function Home() {
|
|
10913
|
+
const { data: session, isPending } = authClient.useSession();
|
|
10914
|
+
|
|
10915
|
+
const { data: hello } = useQuery({
|
|
10916
|
+
queryKey: ['hello'],
|
|
10917
|
+
queryFn: () => fetch('/api/hello').then((r) => r.json()),
|
|
10918
|
+
});
|
|
10919
|
+
|
|
10920
|
+
return (
|
|
10921
|
+
<main className="max-w-lg mx-auto min-h-full px-4 py-8 space-y-4">
|
|
10922
|
+
<div className="pb-4 border-b flex items-start justify-between">
|
|
10923
|
+
<div>
|
|
10924
|
+
<p className="font-mono text-sm text-muted-foreground">apps/web/page.tsx</p>
|
|
10925
|
+
<p className="text-sm text-muted-foreground">Delete me to get started!</p>
|
|
10926
|
+
</div>
|
|
10927
|
+
<ModeToggle />
|
|
10928
|
+
</div>
|
|
10929
|
+
|
|
10930
|
+
<div className="flex items-center gap-3 py-2">
|
|
10931
|
+
<Logo className="w-10 h-auto text-foreground" />
|
|
10932
|
+
<div>
|
|
10933
|
+
<h1 className="text-4xl font-black tracking-tight"><%= projectName %></h1>
|
|
10934
|
+
<p className="text-sm text-muted-foreground tracking-widest uppercase">
|
|
10935
|
+
Next + Nest = Newt \u{1F49C}
|
|
10936
|
+
</p>
|
|
10937
|
+
</div>
|
|
10938
|
+
</div>
|
|
10939
|
+
|
|
10940
|
+
<Card>
|
|
10941
|
+
<CardHeader>
|
|
10942
|
+
<CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
|
|
10943
|
+
next.js
|
|
10944
|
+
</CardTitle>
|
|
10945
|
+
</CardHeader>
|
|
10946
|
+
<CardContent>
|
|
10947
|
+
<p className="font-mono text-sm text-muted-foreground">apps/web/layout.tsx</p>
|
|
10948
|
+
<p className="text-sm text-muted-foreground">Next.js rendering</p>
|
|
10949
|
+
</CardContent>
|
|
10950
|
+
</Card>
|
|
10951
|
+
|
|
10952
|
+
<Card>
|
|
10953
|
+
<CardHeader>
|
|
10954
|
+
<CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
|
|
10955
|
+
nest.js
|
|
10956
|
+
</CardTitle>
|
|
10957
|
+
</CardHeader>
|
|
10958
|
+
<CardContent>
|
|
10959
|
+
<p className="font-mono text-sm text-muted-foreground">GET /api/hello</p>
|
|
10960
|
+
<pre className="mt-2 rounded-md border p-3 text-sm bg-muted/50">
|
|
10961
|
+
<code>{JSON.stringify(hello, null, 2)}</code>
|
|
10962
|
+
</pre>
|
|
10963
|
+
</CardContent>
|
|
10964
|
+
</Card>
|
|
10965
|
+
|
|
10966
|
+
<Card>
|
|
10967
|
+
<CardHeader>
|
|
10968
|
+
<CardTitle className="text-xs font-mono uppercase tracking-widest text-muted-foreground">
|
|
10969
|
+
better-auth
|
|
10970
|
+
</CardTitle>
|
|
10971
|
+
</CardHeader>
|
|
10972
|
+
<CardContent>
|
|
10973
|
+
{isPending ? (
|
|
10974
|
+
<p className="text-sm text-muted-foreground">Loading\u2026</p>
|
|
10975
|
+
) : session ? (
|
|
10976
|
+
<TodoList session={session} />
|
|
10977
|
+
) : (
|
|
10978
|
+
<AuthForm />
|
|
10979
|
+
)}
|
|
10980
|
+
</CardContent>
|
|
10981
|
+
</Card>
|
|
10982
|
+
|
|
10983
|
+
<div className="px-2 py-4 text-sm">
|
|
10984
|
+
<p className="mb-2 text-muted-foreground">Learn more</p>
|
|
10985
|
+
<ul className="list-disc list-inside space-y-2 mt-2">
|
|
10986
|
+
<li>
|
|
10987
|
+
<Link href="https://github.com">GitHub</Link>
|
|
10988
|
+
</li>
|
|
10989
|
+
<li>
|
|
10990
|
+
<Link href="https://nextjs.org">Next.js</Link>
|
|
10991
|
+
</li>
|
|
10992
|
+
<li>
|
|
10993
|
+
<Link href="https://nestjs.com">NestJS</Link>
|
|
10994
|
+
</li>
|
|
10995
|
+
</ul>
|
|
10996
|
+
</div>
|
|
10997
|
+
</main>
|
|
10998
|
+
);
|
|
10999
|
+
}`
|
|
11000
|
+
};
|
|
11001
|
+
|
|
11002
|
+
// src/todo-example/index.ts
|
|
11003
|
+
var todoExampleApi = {
|
|
11004
|
+
templates: [todos_module_default, todos_service_default, todos_service_spec_default]
|
|
11005
|
+
};
|
|
11006
|
+
var todoExampleControllers = {
|
|
11007
|
+
templates: [todos_controller_default, todos_module_controllers_default, app_module_controllers_default]
|
|
11008
|
+
};
|
|
11009
|
+
var todoExampleDi = {
|
|
11010
|
+
templates: [web_todos_route_default, web_todos_id_route_default, web_todos_toggle_route_default, app_module_di_default]
|
|
11011
|
+
};
|
|
11012
|
+
var todoExampleWeb = {
|
|
11013
|
+
templates: [web_todo_list_default, web_page_default]
|
|
11014
|
+
};
|
|
11015
|
+
var todoExampleShadcn = {
|
|
11016
|
+
templates: [shadcn_todo_list_default, shadcn_page_default]
|
|
11017
|
+
};
|
|
10786
11018
|
|
|
10787
11019
|
// src/index.ts
|
|
10788
11020
|
var staticDir = new URL("./static/", import.meta.url);
|
|
@@ -10805,7 +11037,12 @@ var templates = {
|
|
|
10805
11037
|
deploymentCustomServer: single_process_custom_server_default,
|
|
10806
11038
|
deploymentSpa: single_process_static_export_default,
|
|
10807
11039
|
nestDiOnly: nest_di_only_default,
|
|
10808
|
-
apiControllers: api_controllers_default
|
|
11040
|
+
apiControllers: api_controllers_default,
|
|
11041
|
+
todoExampleApi,
|
|
11042
|
+
todoExampleControllers,
|
|
11043
|
+
todoExampleDi,
|
|
11044
|
+
todoExampleWeb,
|
|
11045
|
+
todoExampleShadcn
|
|
10809
11046
|
};
|
|
10810
11047
|
export {
|
|
10811
11048
|
getStaticFilePath,
|