archetype-engine 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +241 -0
- package/dist/src/ai/adapters/anthropic.d.ts +31 -0
- package/dist/src/ai/adapters/anthropic.d.ts.map +1 -0
- package/dist/src/ai/adapters/anthropic.js +75 -0
- package/dist/src/ai/adapters/openai.d.ts +33 -0
- package/dist/src/ai/adapters/openai.d.ts.map +1 -0
- package/dist/src/ai/adapters/openai.js +120 -0
- package/dist/src/ai/adapters/vercel.d.ts +434 -0
- package/dist/src/ai/adapters/vercel.d.ts.map +1 -0
- package/dist/src/ai/adapters/vercel.js +162 -0
- package/dist/src/ai/index.d.ts +492 -0
- package/dist/src/ai/index.d.ts.map +1 -0
- package/dist/src/ai/index.js +71 -0
- package/dist/src/ai/state.d.ts +13 -0
- package/dist/src/ai/state.d.ts.map +1 -0
- package/dist/src/ai/state.js +215 -0
- package/dist/src/ai/tools.d.ts +13 -0
- package/dist/src/ai/tools.d.ts.map +1 -0
- package/dist/src/ai/tools.js +257 -0
- package/dist/src/ai/types.d.ts +196 -0
- package/dist/src/ai/types.d.ts.map +1 -0
- package/dist/src/ai/types.js +9 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +540 -0
- package/dist/src/core/utils.d.ts +27 -0
- package/dist/src/core/utils.d.ts.map +1 -0
- package/dist/src/core/utils.js +56 -0
- package/dist/src/entity.d.ts +165 -0
- package/dist/src/entity.d.ts.map +1 -0
- package/dist/src/entity.js +108 -0
- package/dist/src/fields.d.ts +207 -0
- package/dist/src/fields.d.ts.map +1 -0
- package/dist/src/fields.js +291 -0
- package/dist/src/generators/erd-ir.d.ts +10 -0
- package/dist/src/generators/erd-ir.d.ts.map +1 -0
- package/dist/src/generators/erd-ir.js +119 -0
- package/dist/src/index.d.ts +51 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +101 -0
- package/dist/src/init/dependencies.d.ts +31 -0
- package/dist/src/init/dependencies.d.ts.map +1 -0
- package/dist/src/init/dependencies.js +101 -0
- package/dist/src/init/entity-templates.d.ts +42 -0
- package/dist/src/init/entity-templates.d.ts.map +1 -0
- package/dist/src/init/entity-templates.js +367 -0
- package/dist/src/init/index.d.ts +10 -0
- package/dist/src/init/index.d.ts.map +1 -0
- package/dist/src/init/index.js +250 -0
- package/dist/src/init/prompts.d.ts +11 -0
- package/dist/src/init/prompts.d.ts.map +1 -0
- package/dist/src/init/prompts.js +275 -0
- package/dist/src/init/templates.d.ts +24 -0
- package/dist/src/init/templates.d.ts.map +1 -0
- package/dist/src/init/templates.js +587 -0
- package/dist/src/json/index.d.ts +11 -0
- package/dist/src/json/index.d.ts.map +1 -0
- package/dist/src/json/index.js +26 -0
- package/dist/src/json/parser.d.ts +61 -0
- package/dist/src/json/parser.d.ts.map +1 -0
- package/dist/src/json/parser.js +309 -0
- package/dist/src/json/types.d.ts +275 -0
- package/dist/src/json/types.d.ts.map +1 -0
- package/dist/src/json/types.js +10 -0
- package/dist/src/manifest.d.ts +147 -0
- package/dist/src/manifest.d.ts.map +1 -0
- package/dist/src/manifest.js +104 -0
- package/dist/src/relations.d.ts +96 -0
- package/dist/src/relations.d.ts.map +1 -0
- package/dist/src/relations.js +108 -0
- package/dist/src/source.d.ts +93 -0
- package/dist/src/source.d.ts.map +1 -0
- package/dist/src/source.js +89 -0
- package/dist/src/template/context.d.ts +34 -0
- package/dist/src/template/context.d.ts.map +1 -0
- package/dist/src/template/context.js +31 -0
- package/dist/src/template/index.d.ts +6 -0
- package/dist/src/template/index.d.ts.map +1 -0
- package/dist/src/template/index.js +12 -0
- package/dist/src/template/registry.d.ts +18 -0
- package/dist/src/template/registry.d.ts.map +1 -0
- package/dist/src/template/registry.js +89 -0
- package/dist/src/template/runner.d.ts +9 -0
- package/dist/src/template/runner.d.ts.map +1 -0
- package/dist/src/template/runner.js +125 -0
- package/dist/src/template/types.d.ts +73 -0
- package/dist/src/template/types.d.ts.map +1 -0
- package/dist/src/template/types.js +3 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/api.d.ts +22 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/api.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/api.js +866 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/auth.d.ts +20 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/auth.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/auth.js +273 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/crud-hooks.d.ts +22 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/crud-hooks.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/crud-hooks.js +237 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/hooks.d.ts +30 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/hooks.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/hooks.js +345 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/i18n.d.ts +25 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/i18n.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/i18n.js +199 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts +8 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/index.js +18 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/schema.d.ts +22 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/schema.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/schema.js +270 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/service.d.ts +23 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/service.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/service.js +304 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/validation.d.ts +21 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/validation.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/generators/validation.js +248 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts +30 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.d.ts.map +1 -0
- package/dist/src/templates/nextjs-drizzle-trpc/index.js +71 -0
- package/dist/src/validation/index.d.ts +71 -0
- package/dist/src/validation/index.d.ts.map +1 -0
- package/dist/src/validation/index.js +314 -0
- package/package.json +86 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* React Hooks Generator
|
|
4
|
+
*
|
|
5
|
+
* Generates React hooks for data fetching and form handling using tRPC and React Hook Form.
|
|
6
|
+
* Provides a complete set of hooks for each entity's CRUD operations.
|
|
7
|
+
*
|
|
8
|
+
* Generated files:
|
|
9
|
+
* - hooks/use{Entity}.ts - Hooks for list, get, create, edit, and remove operations
|
|
10
|
+
*
|
|
11
|
+
* Generated hooks per entity:
|
|
12
|
+
* - use{Entity}s() - List all entities (useQuery)
|
|
13
|
+
* - use{Entity}(id) - Get single entity by ID (useQuery)
|
|
14
|
+
* - use{Entity}Form() - Create form with validation and mutation
|
|
15
|
+
* - use{Entity}EditForm(id) - Edit form with data loading and mutation
|
|
16
|
+
* - use{Entity}Remove() - Delete mutation with cache invalidation
|
|
17
|
+
* - useCreate{Entity}() - Create mutation without form
|
|
18
|
+
* - useUpdate{Entity}() - Update mutation without form
|
|
19
|
+
*
|
|
20
|
+
* Features:
|
|
21
|
+
* - Zod validation integration via zodResolver
|
|
22
|
+
* - Automatic tRPC cache invalidation on mutations
|
|
23
|
+
* - i18n support for validation messages when multiple languages configured
|
|
24
|
+
* - Form reset on successful create
|
|
25
|
+
* - Null-to-undefined conversion for form compatibility
|
|
26
|
+
*
|
|
27
|
+
* @module generators/hooks
|
|
28
|
+
*/
|
|
29
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
+
exports.hooksGenerator = void 0;
|
|
31
|
+
/**
|
|
32
|
+
* Generate filter type definitions for an entity
|
|
33
|
+
*/
|
|
34
|
+
function generateFilterTypes(entity) {
|
|
35
|
+
const filterFields = Object.entries(entity.fields).map(([fieldName, field]) => {
|
|
36
|
+
switch (field.type) {
|
|
37
|
+
case 'text':
|
|
38
|
+
return ` ${fieldName}?: string | {
|
|
39
|
+
eq?: string
|
|
40
|
+
ne?: string
|
|
41
|
+
contains?: string
|
|
42
|
+
startsWith?: string
|
|
43
|
+
endsWith?: string
|
|
44
|
+
}`;
|
|
45
|
+
case 'number':
|
|
46
|
+
return ` ${fieldName}?: number | {
|
|
47
|
+
eq?: number
|
|
48
|
+
ne?: number
|
|
49
|
+
gt?: number
|
|
50
|
+
gte?: number
|
|
51
|
+
lt?: number
|
|
52
|
+
lte?: number
|
|
53
|
+
}`;
|
|
54
|
+
case 'boolean':
|
|
55
|
+
return ` ${fieldName}?: boolean`;
|
|
56
|
+
case 'date':
|
|
57
|
+
return ` ${fieldName}?: string | {
|
|
58
|
+
eq?: string
|
|
59
|
+
ne?: string
|
|
60
|
+
gt?: string
|
|
61
|
+
gte?: string
|
|
62
|
+
lt?: string
|
|
63
|
+
lte?: string
|
|
64
|
+
}`;
|
|
65
|
+
default:
|
|
66
|
+
return ` ${fieldName}?: string`;
|
|
67
|
+
}
|
|
68
|
+
}).join('\n');
|
|
69
|
+
// Get field names for orderBy
|
|
70
|
+
const fieldNames = Object.keys(entity.fields);
|
|
71
|
+
const orderByFields = [...fieldNames, 'createdAt', 'updatedAt'];
|
|
72
|
+
return `// Filter operators for ${entity.name}
|
|
73
|
+
export interface ${entity.name}Filter {
|
|
74
|
+
${filterFields}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// OrderBy options for ${entity.name}
|
|
78
|
+
export interface ${entity.name}OrderBy {
|
|
79
|
+
field: ${orderByFields.map(f => `'${f}'`).join(' | ')}
|
|
80
|
+
direction?: 'asc' | 'desc'
|
|
81
|
+
}`;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* JavaScript reserved words that can't be used as variable names
|
|
85
|
+
*/
|
|
86
|
+
const JS_RESERVED_WORDS = new Set([
|
|
87
|
+
'break', 'case', 'catch', 'continue', 'debugger', 'default', 'delete',
|
|
88
|
+
'do', 'else', 'finally', 'for', 'function', 'if', 'in', 'instanceof',
|
|
89
|
+
'new', 'return', 'switch', 'this', 'throw', 'try', 'typeof', 'var',
|
|
90
|
+
'void', 'while', 'with', 'class', 'const', 'enum', 'export', 'extends',
|
|
91
|
+
'import', 'super', 'implements', 'interface', 'let', 'package', 'private',
|
|
92
|
+
'protected', 'public', 'static', 'yield', 'await', 'null', 'undefined', 'true', 'false'
|
|
93
|
+
]);
|
|
94
|
+
/**
|
|
95
|
+
* Get a safe variable name, prefixing with underscore if it's a reserved word
|
|
96
|
+
*/
|
|
97
|
+
function getSafeVarName(name) {
|
|
98
|
+
return JS_RESERVED_WORDS.has(name.toLowerCase()) ? `_${name.toLowerCase()}` : name.toLowerCase();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Generate complete hooks file for an entity
|
|
102
|
+
*
|
|
103
|
+
* @param entity - Entity IR with name and field definitions
|
|
104
|
+
* @param manifest - Manifest IR with i18n configuration
|
|
105
|
+
* @returns Complete hooks file content as string
|
|
106
|
+
*/
|
|
107
|
+
function generateEntityHooks(entity, manifest) {
|
|
108
|
+
const name = entity.name;
|
|
109
|
+
const lowerName = name.toLowerCase();
|
|
110
|
+
const safeVarName = getSafeVarName(name);
|
|
111
|
+
const useI18n = manifest.i18n.languages.length > 1;
|
|
112
|
+
// Schema imports
|
|
113
|
+
const schemaImport = useI18n
|
|
114
|
+
? `import { ${lowerName}CreateSchemaI18n, ${lowerName}UpdateSchemaI18n, ${name}Create, ${name}Update } from '@/generated/schemas/${lowerName}'`
|
|
115
|
+
: `import { ${lowerName}CreateSchema, ${lowerName}UpdateSchema, ${name}Create, ${name}Update } from '@/generated/schemas/${lowerName}'`;
|
|
116
|
+
const i18nImport = useI18n
|
|
117
|
+
? `import { useTranslations } from 'next-intl'`
|
|
118
|
+
: '';
|
|
119
|
+
const tDeclaration = useI18n
|
|
120
|
+
? ` const t = useTranslations('validation')`
|
|
121
|
+
: '';
|
|
122
|
+
const resolverSchema = useI18n
|
|
123
|
+
? `${lowerName}CreateSchemaI18n(t)`
|
|
124
|
+
: `${lowerName}CreateSchema`;
|
|
125
|
+
const updateResolverSchema = useI18n
|
|
126
|
+
? `${lowerName}UpdateSchemaI18n(t)`
|
|
127
|
+
: `${lowerName}UpdateSchema`;
|
|
128
|
+
// Generate filter types
|
|
129
|
+
const filterTypes = generateFilterTypes(entity);
|
|
130
|
+
return `// Auto-generated hooks for ${name}
|
|
131
|
+
// Do not edit manually - regenerate with: npx archetype generate
|
|
132
|
+
|
|
133
|
+
'use client'
|
|
134
|
+
|
|
135
|
+
import { useMemo } from 'react'
|
|
136
|
+
import { useForm } from 'react-hook-form'
|
|
137
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
138
|
+
import { trpc } from '@/lib/trpc'
|
|
139
|
+
${i18nImport}
|
|
140
|
+
${schemaImport}
|
|
141
|
+
|
|
142
|
+
// Convert null values to undefined for form compatibility
|
|
143
|
+
type NullToUndefined<T> = {
|
|
144
|
+
[K in keyof T]: T[K] extends null ? undefined : Exclude<T[K], null>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function nullToUndefined<T extends Record<string, unknown>>(
|
|
148
|
+
obj: T | null | undefined
|
|
149
|
+
): NullToUndefined<T> | undefined {
|
|
150
|
+
if (!obj) return undefined
|
|
151
|
+
return Object.fromEntries(
|
|
152
|
+
Object.entries(obj).map(([k, v]) => [k, v === null ? undefined : v])
|
|
153
|
+
) as NullToUndefined<T>
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
${filterTypes}
|
|
157
|
+
|
|
158
|
+
// ============ LIST WITH PAGINATION, FILTERING, SORTING, AND SEARCH ============
|
|
159
|
+
export interface Use${name}sOptions {
|
|
160
|
+
// Pagination
|
|
161
|
+
page?: number
|
|
162
|
+
limit?: number
|
|
163
|
+
// Filtering
|
|
164
|
+
where?: ${name}Filter
|
|
165
|
+
// Sorting
|
|
166
|
+
orderBy?: ${name}OrderBy
|
|
167
|
+
// Search across text fields
|
|
168
|
+
search?: string
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// React Query options for controlling query behavior
|
|
172
|
+
export interface QueryOptions {
|
|
173
|
+
enabled?: boolean
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function use${name}s(options?: Use${name}sOptions, queryOptions?: QueryOptions) {
|
|
177
|
+
return trpc.${lowerName}.list.useQuery({
|
|
178
|
+
page: options?.page ?? 1,
|
|
179
|
+
limit: options?.limit ?? 20,
|
|
180
|
+
where: options?.where,
|
|
181
|
+
orderBy: options?.orderBy,
|
|
182
|
+
search: options?.search,
|
|
183
|
+
}, queryOptions)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ============ GET ============
|
|
187
|
+
export function use${name}(id: string) {
|
|
188
|
+
return trpc.${lowerName}.get.useQuery({ id }, {
|
|
189
|
+
enabled: !!id,
|
|
190
|
+
})
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ============ CREATE FORM ============
|
|
194
|
+
export function use${name}Form() {
|
|
195
|
+
${tDeclaration}
|
|
196
|
+
const utils = trpc.useUtils()
|
|
197
|
+
|
|
198
|
+
const form = useForm<${name}Create>({
|
|
199
|
+
resolver: zodResolver(${resolverSchema}),
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const mutation = trpc.${lowerName}.create.useMutation({
|
|
203
|
+
onSuccess: () => {
|
|
204
|
+
utils.${lowerName}.list.invalidate()
|
|
205
|
+
form.reset()
|
|
206
|
+
},
|
|
207
|
+
})
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
...form,
|
|
211
|
+
submit: form.handleSubmit((data) => mutation.mutate(data)),
|
|
212
|
+
isPending: mutation.isPending,
|
|
213
|
+
error: mutation.error,
|
|
214
|
+
isSuccess: mutation.isSuccess,
|
|
215
|
+
reset: () => {
|
|
216
|
+
form.reset()
|
|
217
|
+
mutation.reset()
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ============ EDIT FORM ============
|
|
223
|
+
export function use${name}EditForm(id: string) {
|
|
224
|
+
${tDeclaration}
|
|
225
|
+
const utils = trpc.useUtils()
|
|
226
|
+
const { data: ${safeVarName}, isLoading } = trpc.${lowerName}.get.useQuery({ id })
|
|
227
|
+
|
|
228
|
+
const mutation = trpc.${lowerName}.update.useMutation({
|
|
229
|
+
onSuccess: () => {
|
|
230
|
+
utils.${lowerName}.invalidate()
|
|
231
|
+
},
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const formValues = useMemo(() => nullToUndefined(${safeVarName}) as ${name}Update | undefined, [${safeVarName}])
|
|
235
|
+
|
|
236
|
+
const form = useForm<${name}Update>({
|
|
237
|
+
resolver: zodResolver(${updateResolverSchema}),
|
|
238
|
+
values: formValues,
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
...form,
|
|
243
|
+
submit: form.handleSubmit((data) => mutation.mutate({ id, data })),
|
|
244
|
+
isPending: mutation.isPending,
|
|
245
|
+
isLoading,
|
|
246
|
+
error: mutation.error,
|
|
247
|
+
isSuccess: mutation.isSuccess,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// ============ REMOVE ============
|
|
252
|
+
export function use${name}Remove() {
|
|
253
|
+
const utils = trpc.useUtils()
|
|
254
|
+
const mutation = trpc.${lowerName}.remove.useMutation({
|
|
255
|
+
onSuccess: () => {
|
|
256
|
+
utils.${lowerName}.list.invalidate()
|
|
257
|
+
},
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
remove: (id: string) => mutation.mutate({ id }),
|
|
262
|
+
isPending: mutation.isPending,
|
|
263
|
+
error: mutation.error,
|
|
264
|
+
isSuccess: mutation.isSuccess,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ============ CREATE MUTATION (without form) ============
|
|
269
|
+
export function useCreate${name}() {
|
|
270
|
+
const utils = trpc.useUtils()
|
|
271
|
+
return trpc.${lowerName}.create.useMutation({
|
|
272
|
+
onSuccess: () => {
|
|
273
|
+
utils.${lowerName}.list.invalidate()
|
|
274
|
+
},
|
|
275
|
+
})
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ============ UPDATE MUTATION (without form) ============
|
|
279
|
+
export function useUpdate${name}() {
|
|
280
|
+
const utils = trpc.useUtils()
|
|
281
|
+
return trpc.${lowerName}.update.useMutation({
|
|
282
|
+
onSuccess: () => {
|
|
283
|
+
utils.${lowerName}.invalidate()
|
|
284
|
+
},
|
|
285
|
+
})
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// ============ BATCH OPERATIONS ============
|
|
289
|
+
|
|
290
|
+
// Create multiple ${name}s at once
|
|
291
|
+
export function useCreateMany${name}s() {
|
|
292
|
+
const utils = trpc.useUtils()
|
|
293
|
+
const mutation = trpc.${lowerName}.createMany.useMutation({
|
|
294
|
+
onSuccess: () => {
|
|
295
|
+
utils.${lowerName}.list.invalidate()
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
createMany: (items: ${name}Create[]) => mutation.mutate({ items }),
|
|
301
|
+
...mutation,
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Update multiple ${name}s at once
|
|
306
|
+
export function useUpdateMany${name}s() {
|
|
307
|
+
const utils = trpc.useUtils()
|
|
308
|
+
const mutation = trpc.${lowerName}.updateMany.useMutation({
|
|
309
|
+
onSuccess: () => {
|
|
310
|
+
utils.${lowerName}.invalidate()
|
|
311
|
+
},
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
updateMany: (items: { id: string; data: ${name}Update }[]) => mutation.mutate({ items }),
|
|
316
|
+
...mutation,
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Remove multiple ${name}s at once
|
|
321
|
+
export function useRemoveMany${name}s() {
|
|
322
|
+
const utils = trpc.useUtils()
|
|
323
|
+
const mutation = trpc.${lowerName}.removeMany.useMutation({
|
|
324
|
+
onSuccess: () => {
|
|
325
|
+
utils.${lowerName}.list.invalidate()
|
|
326
|
+
},
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
removeMany: (ids: string[]) => mutation.mutate({ ids }),
|
|
331
|
+
...mutation,
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
`;
|
|
335
|
+
}
|
|
336
|
+
exports.hooksGenerator = {
|
|
337
|
+
name: 'react-hooks',
|
|
338
|
+
description: 'Generate React hooks with React Hook Form integration',
|
|
339
|
+
generate(manifest, ctx) {
|
|
340
|
+
return manifest.entities.map(entity => ({
|
|
341
|
+
path: `hooks/use${entity.name}.ts`,
|
|
342
|
+
content: generateEntityHooks(entity, manifest),
|
|
343
|
+
}));
|
|
344
|
+
},
|
|
345
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* i18n Translation Files Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates JSON translation files for internationalized validation messages.
|
|
5
|
+
* Only runs when multiple languages are configured in the manifest.
|
|
6
|
+
*
|
|
7
|
+
* Generated files:
|
|
8
|
+
* - i18n/{lang}/validation.json - Validation error messages
|
|
9
|
+
* - i18n/{lang}/fields.json - Field labels per entity
|
|
10
|
+
* - i18n/{lang}/entities.json - Entity display names
|
|
11
|
+
*
|
|
12
|
+
* Supported languages (with built-in translations):
|
|
13
|
+
* - en (English), es (Spanish), fr (French), de (German)
|
|
14
|
+
* - pt (Portuguese), it (Italian), ja (Japanese), zh (Chinese), ko (Korean)
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Parameterized message templates ({field}, {min}, {max}, {values})
|
|
18
|
+
* - Compatible with next-intl ICU message format
|
|
19
|
+
* - Falls back to English for unsupported languages
|
|
20
|
+
*
|
|
21
|
+
* @module generators/i18n
|
|
22
|
+
*/
|
|
23
|
+
import type { Generator } from '../../../template/types';
|
|
24
|
+
export declare const i18nGenerator: Generator;
|
|
25
|
+
//# sourceMappingURL=i18n.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/i18n.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAoJvE,eAAO,MAAM,aAAa,EAAE,SAwC3B,CAAA"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* i18n Translation Files Generator
|
|
4
|
+
*
|
|
5
|
+
* Generates JSON translation files for internationalized validation messages.
|
|
6
|
+
* Only runs when multiple languages are configured in the manifest.
|
|
7
|
+
*
|
|
8
|
+
* Generated files:
|
|
9
|
+
* - i18n/{lang}/validation.json - Validation error messages
|
|
10
|
+
* - i18n/{lang}/fields.json - Field labels per entity
|
|
11
|
+
* - i18n/{lang}/entities.json - Entity display names
|
|
12
|
+
*
|
|
13
|
+
* Supported languages (with built-in translations):
|
|
14
|
+
* - en (English), es (Spanish), fr (French), de (German)
|
|
15
|
+
* - pt (Portuguese), it (Italian), ja (Japanese), zh (Chinese), ko (Korean)
|
|
16
|
+
*
|
|
17
|
+
* Features:
|
|
18
|
+
* - Parameterized message templates ({field}, {min}, {max}, {values})
|
|
19
|
+
* - Compatible with next-intl ICU message format
|
|
20
|
+
* - Falls back to English for unsupported languages
|
|
21
|
+
*
|
|
22
|
+
* @module generators/i18n
|
|
23
|
+
*/
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.i18nGenerator = void 0;
|
|
26
|
+
// Default validation messages per language
|
|
27
|
+
const defaultValidationMessages = {
|
|
28
|
+
en: {
|
|
29
|
+
required: '{field} is required',
|
|
30
|
+
email: 'Invalid email address',
|
|
31
|
+
url: 'Invalid URL',
|
|
32
|
+
min: '{field} must be at least {min}',
|
|
33
|
+
max: '{field} must be at most {max}',
|
|
34
|
+
minLength: '{field} must be at least {min} characters',
|
|
35
|
+
maxLength: '{field} must be at most {max} characters',
|
|
36
|
+
oneOf: '{field} must be one of: {values}',
|
|
37
|
+
pattern: '{field} format is invalid',
|
|
38
|
+
integer: '{field} must be a whole number',
|
|
39
|
+
positive: '{field} must be positive',
|
|
40
|
+
},
|
|
41
|
+
es: {
|
|
42
|
+
required: '{field} es requerido',
|
|
43
|
+
email: 'Correo electrónico inválido',
|
|
44
|
+
url: 'URL inválida',
|
|
45
|
+
min: '{field} debe ser al menos {min}',
|
|
46
|
+
max: '{field} debe ser como máximo {max}',
|
|
47
|
+
minLength: '{field} debe tener al menos {min} caracteres',
|
|
48
|
+
maxLength: '{field} debe tener como máximo {max} caracteres',
|
|
49
|
+
oneOf: '{field} debe ser uno de: {values}',
|
|
50
|
+
pattern: 'Formato de {field} inválido',
|
|
51
|
+
integer: '{field} debe ser un número entero',
|
|
52
|
+
positive: '{field} debe ser positivo',
|
|
53
|
+
},
|
|
54
|
+
fr: {
|
|
55
|
+
required: '{field} est requis',
|
|
56
|
+
email: 'Adresse e-mail invalide',
|
|
57
|
+
url: 'URL invalide',
|
|
58
|
+
min: '{field} doit être au moins {min}',
|
|
59
|
+
max: '{field} doit être au plus {max}',
|
|
60
|
+
minLength: '{field} doit contenir au moins {min} caractères',
|
|
61
|
+
maxLength: '{field} doit contenir au plus {max} caractères',
|
|
62
|
+
oneOf: '{field} doit être l\'un des: {values}',
|
|
63
|
+
pattern: 'Format de {field} invalide',
|
|
64
|
+
integer: '{field} doit être un nombre entier',
|
|
65
|
+
positive: '{field} doit être positif',
|
|
66
|
+
},
|
|
67
|
+
de: {
|
|
68
|
+
required: '{field} ist erforderlich',
|
|
69
|
+
email: 'Ungültige E-Mail-Adresse',
|
|
70
|
+
url: 'Ungültige URL',
|
|
71
|
+
min: '{field} muss mindestens {min} sein',
|
|
72
|
+
max: '{field} darf höchstens {max} sein',
|
|
73
|
+
minLength: '{field} muss mindestens {min} Zeichen haben',
|
|
74
|
+
maxLength: '{field} darf höchstens {max} Zeichen haben',
|
|
75
|
+
oneOf: '{field} muss eines von: {values} sein',
|
|
76
|
+
pattern: 'Format von {field} ist ungültig',
|
|
77
|
+
integer: '{field} muss eine ganze Zahl sein',
|
|
78
|
+
positive: '{field} muss positiv sein',
|
|
79
|
+
},
|
|
80
|
+
pt: {
|
|
81
|
+
required: '{field} é obrigatório',
|
|
82
|
+
email: 'Endereço de e-mail inválido',
|
|
83
|
+
url: 'URL inválida',
|
|
84
|
+
min: '{field} deve ser pelo menos {min}',
|
|
85
|
+
max: '{field} deve ser no máximo {max}',
|
|
86
|
+
minLength: '{field} deve ter pelo menos {min} caracteres',
|
|
87
|
+
maxLength: '{field} deve ter no máximo {max} caracteres',
|
|
88
|
+
oneOf: '{field} deve ser um de: {values}',
|
|
89
|
+
pattern: 'Formato de {field} inválido',
|
|
90
|
+
integer: '{field} deve ser um número inteiro',
|
|
91
|
+
positive: '{field} deve ser positivo',
|
|
92
|
+
},
|
|
93
|
+
it: {
|
|
94
|
+
required: '{field} è richiesto',
|
|
95
|
+
email: 'Indirizzo email non valido',
|
|
96
|
+
url: 'URL non valido',
|
|
97
|
+
min: '{field} deve essere almeno {min}',
|
|
98
|
+
max: '{field} deve essere al massimo {max}',
|
|
99
|
+
minLength: '{field} deve contenere almeno {min} caratteri',
|
|
100
|
+
maxLength: '{field} deve contenere al massimo {max} caratteri',
|
|
101
|
+
oneOf: '{field} deve essere uno tra: {values}',
|
|
102
|
+
pattern: 'Formato di {field} non valido',
|
|
103
|
+
integer: '{field} deve essere un numero intero',
|
|
104
|
+
positive: '{field} deve essere positivo',
|
|
105
|
+
},
|
|
106
|
+
ja: {
|
|
107
|
+
required: '{field}は必須です',
|
|
108
|
+
email: '無効なメールアドレス',
|
|
109
|
+
url: '無効なURL',
|
|
110
|
+
min: '{field}は{min}以上である必要があります',
|
|
111
|
+
max: '{field}は{max}以下である必要があります',
|
|
112
|
+
minLength: '{field}は{min}文字以上である必要があります',
|
|
113
|
+
maxLength: '{field}は{max}文字以下である必要があります',
|
|
114
|
+
oneOf: '{field}は次のいずれかである必要があります: {values}',
|
|
115
|
+
pattern: '{field}の形式が無効です',
|
|
116
|
+
integer: '{field}は整数である必要があります',
|
|
117
|
+
positive: '{field}は正の数である必要があります',
|
|
118
|
+
},
|
|
119
|
+
zh: {
|
|
120
|
+
required: '{field}是必填项',
|
|
121
|
+
email: '无效的电子邮件地址',
|
|
122
|
+
url: '无效的URL',
|
|
123
|
+
min: '{field}必须至少为{min}',
|
|
124
|
+
max: '{field}必须最多为{max}',
|
|
125
|
+
minLength: '{field}必须至少为{min}个字符',
|
|
126
|
+
maxLength: '{field}必须最多为{max}个字符',
|
|
127
|
+
oneOf: '{field}必须是以下之一: {values}',
|
|
128
|
+
pattern: '{field}格式无效',
|
|
129
|
+
integer: '{field}必须是整数',
|
|
130
|
+
positive: '{field}必须是正数',
|
|
131
|
+
},
|
|
132
|
+
ko: {
|
|
133
|
+
required: '{field}은(는) 필수입니다',
|
|
134
|
+
email: '유효하지 않은 이메일 주소',
|
|
135
|
+
url: '유효하지 않은 URL',
|
|
136
|
+
min: '{field}은(는) 최소 {min}이어야 합니다',
|
|
137
|
+
max: '{field}은(는) 최대 {max}이어야 합니다',
|
|
138
|
+
minLength: '{field}은(는) 최소 {min}자 이상이어야 합니다',
|
|
139
|
+
maxLength: '{field}은(는) 최대 {max}자 이하이어야 합니다',
|
|
140
|
+
oneOf: '{field}은(는) 다음 중 하나여야 합니다: {values}',
|
|
141
|
+
pattern: '{field} 형식이 유효하지 않습니다',
|
|
142
|
+
integer: '{field}은(는) 정수여야 합니다',
|
|
143
|
+
positive: '{field}은(는) 양수여야 합니다',
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Generate field labels mapping from entities
|
|
148
|
+
*
|
|
149
|
+
* Extracts labels from field configurations, falling back to field names.
|
|
150
|
+
*
|
|
151
|
+
* @param entities - Array of entity IRs with field definitions
|
|
152
|
+
* @returns Nested object of entity -> field -> label
|
|
153
|
+
*/
|
|
154
|
+
function generateFieldLabels(entities) {
|
|
155
|
+
const labels = {};
|
|
156
|
+
for (const entity of entities) {
|
|
157
|
+
const entityLabels = {};
|
|
158
|
+
for (const [fieldName, config] of Object.entries(entity.fields)) {
|
|
159
|
+
entityLabels[fieldName] = config.label || fieldName;
|
|
160
|
+
}
|
|
161
|
+
labels[entity.name.toLowerCase()] = entityLabels;
|
|
162
|
+
}
|
|
163
|
+
return labels;
|
|
164
|
+
}
|
|
165
|
+
exports.i18nGenerator = {
|
|
166
|
+
name: 'i18n-files',
|
|
167
|
+
description: 'Generate i18n translation files',
|
|
168
|
+
generate(manifest, ctx) {
|
|
169
|
+
const files = [];
|
|
170
|
+
// Only generate if multiple languages are configured
|
|
171
|
+
if (manifest.i18n.languages.length <= 1) {
|
|
172
|
+
return files;
|
|
173
|
+
}
|
|
174
|
+
for (const lang of manifest.i18n.languages) {
|
|
175
|
+
// Validation messages
|
|
176
|
+
const validationMessages = defaultValidationMessages[lang] || defaultValidationMessages.en;
|
|
177
|
+
files.push({
|
|
178
|
+
path: `i18n/${lang}/validation.json`,
|
|
179
|
+
content: JSON.stringify(validationMessages, null, 2),
|
|
180
|
+
});
|
|
181
|
+
// Field labels
|
|
182
|
+
const fieldLabels = generateFieldLabels(manifest.entities);
|
|
183
|
+
files.push({
|
|
184
|
+
path: `i18n/${lang}/fields.json`,
|
|
185
|
+
content: JSON.stringify(fieldLabels, null, 2),
|
|
186
|
+
});
|
|
187
|
+
// Entity names (for UI)
|
|
188
|
+
const entityNames = {};
|
|
189
|
+
for (const entity of manifest.entities) {
|
|
190
|
+
entityNames[entity.name.toLowerCase()] = entity.name;
|
|
191
|
+
}
|
|
192
|
+
files.push({
|
|
193
|
+
path: `i18n/${lang}/entities.json`,
|
|
194
|
+
content: JSON.stringify(entityNames, null, 2),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return files;
|
|
198
|
+
},
|
|
199
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { schemaGenerator } from './schema';
|
|
2
|
+
export { authGenerator } from './auth';
|
|
3
|
+
export { validationGenerator } from './validation';
|
|
4
|
+
export { apiGenerator } from './api';
|
|
5
|
+
export { hooksGenerator } from './hooks';
|
|
6
|
+
export { crudHooksGenerator } from './crud-hooks';
|
|
7
|
+
export { i18nGenerator } from './i18n';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,OAAO,CAAA;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Export all generators for the nextjs-drizzle-trpc template
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.i18nGenerator = exports.crudHooksGenerator = exports.hooksGenerator = exports.apiGenerator = exports.validationGenerator = exports.authGenerator = exports.schemaGenerator = void 0;
|
|
5
|
+
var schema_1 = require("./schema");
|
|
6
|
+
Object.defineProperty(exports, "schemaGenerator", { enumerable: true, get: function () { return schema_1.schemaGenerator; } });
|
|
7
|
+
var auth_1 = require("./auth");
|
|
8
|
+
Object.defineProperty(exports, "authGenerator", { enumerable: true, get: function () { return auth_1.authGenerator; } });
|
|
9
|
+
var validation_1 = require("./validation");
|
|
10
|
+
Object.defineProperty(exports, "validationGenerator", { enumerable: true, get: function () { return validation_1.validationGenerator; } });
|
|
11
|
+
var api_1 = require("./api");
|
|
12
|
+
Object.defineProperty(exports, "apiGenerator", { enumerable: true, get: function () { return api_1.apiGenerator; } });
|
|
13
|
+
var hooks_1 = require("./hooks");
|
|
14
|
+
Object.defineProperty(exports, "hooksGenerator", { enumerable: true, get: function () { return hooks_1.hooksGenerator; } });
|
|
15
|
+
var crud_hooks_1 = require("./crud-hooks");
|
|
16
|
+
Object.defineProperty(exports, "crudHooksGenerator", { enumerable: true, get: function () { return crud_hooks_1.crudHooksGenerator; } });
|
|
17
|
+
var i18n_1 = require("./i18n");
|
|
18
|
+
Object.defineProperty(exports, "i18nGenerator", { enumerable: true, get: function () { return i18n_1.i18nGenerator; } });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drizzle ORM Schema Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates database table definitions for Drizzle ORM from entity definitions.
|
|
5
|
+
* Supports SQLite, PostgreSQL, and MySQL databases.
|
|
6
|
+
*
|
|
7
|
+
* Generated files:
|
|
8
|
+
* - db/schema.ts - All entity table definitions and junction tables
|
|
9
|
+
*
|
|
10
|
+
* Features:
|
|
11
|
+
* - Maps field types to appropriate database column types
|
|
12
|
+
* - Handles required/optional fields with notNull()
|
|
13
|
+
* - Creates foreign key references for hasOne relations
|
|
14
|
+
* - Generates junction tables for belongsToMany relations
|
|
15
|
+
* - Adds timestamp fields (createdAt, updatedAt) when behaviors.timestamps is true
|
|
16
|
+
* - Adds deletedAt field when behaviors.softDelete is true
|
|
17
|
+
*
|
|
18
|
+
* @module generators/schema
|
|
19
|
+
*/
|
|
20
|
+
import type { Generator } from '../../../template/types';
|
|
21
|
+
export declare const schemaGenerator: Generator;
|
|
22
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../../../src/templates/nextjs-drizzle-trpc/generators/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,yBAAyB,CAAA;AAsPvE,eAAO,MAAM,eAAe,EAAE,SAuC7B,CAAA"}
|