@appswave/rq-codegen 0.1.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/README.md ADDED
@@ -0,0 +1,1279 @@
1
+ # @appswave/rq-codegen
2
+
3
+ Config-driven code generator for React + TypeScript projects. Generates API handlers, React Query hooks, DTO types, components, pages, views, validation schemas, and full features — all wired to your project's aliases, paths, and conventions.
4
+
5
+ ## Table of Contents
6
+
7
+ - [When to Use](#when-to-use)
8
+ - [Quick Start](#quick-start)
9
+ - [CLI Commands](#cli-commands)
10
+ - [Generators](#generators)
11
+ - [handler](#handler)
12
+ - [types-dto](#types-dto)
13
+ - [query-hook](#query-hook)
14
+ - [mutation-hook](#mutation-hook)
15
+ - [component-ui](#component-ui)
16
+ - [component-shared](#component-shared)
17
+ - [component-form](#component-form)
18
+ - [page](#page)
19
+ - [view](#view)
20
+ - [shared-hook](#shared-hook)
21
+ - [validation](#validation)
22
+ - [feature](#feature)
23
+ - [Configuration Reference](#configuration-reference)
24
+ - [srcDir](#srcdir)
25
+ - [aliases](#aliases)
26
+ - [features](#features)
27
+ - [naming](#naming)
28
+ - [paths](#paths)
29
+ - [router](#router)
30
+ - [hooks](#hooks)
31
+ - [templatesDir](#templatesdir)
32
+ - [Feature Toggles](#feature-toggles)
33
+ - [Template Overrides](#template-overrides)
34
+ - [Path Alias Detection](#path-alias-detection)
35
+ - [Recommended Project Structure](#recommended-project-structure)
36
+ - [defineConfig()](#defineconfig)
37
+ - [Contributing](#contributing)
38
+ - [License](#license)
39
+
40
+ ---
41
+
42
+ ## When to Use
43
+
44
+ Use `rq-codegen` every time you need to:
45
+
46
+ - **Add a new API endpoint** — generates the handler, DTO types, query hook, and mutation hook in one command
47
+ - **Create a new page or view** — scaffolds the component with correct folder structure and barrel exports
48
+ - **Build a full feature** — generates everything at once: handler + types + hooks + view + page + validation
49
+ - **Add a new UI component** — creates a CVA-based component with variants (shadcn/ui style)
50
+ - **Add a shared component** — creates a compound component with sub-components (Header, Body, Footer)
51
+ - **Add a form component** — creates a React Hook Form `useController`-based field component
52
+ - **Add a validation schema** — scaffolds a Zod schema with optional i18n support
53
+ - **Add a custom hook** — creates a shared utility hook with selected React imports
54
+
55
+ Every generated file respects your project's path aliases, naming conventions, and feature toggles — no manual find-and-replace needed.
56
+
57
+ ---
58
+
59
+ ## Quick Start
60
+
61
+ ### 1. Install
62
+
63
+ ```bash
64
+ npm install -g @appswave/rq-codegen
65
+ ```
66
+
67
+ Or as a dev dependency:
68
+
69
+ ```bash
70
+ npm install -D @appswave/rq-codegen
71
+ ```
72
+
73
+ ### 2. Initialize Config
74
+
75
+ ```bash
76
+ rq-codegen init
77
+ ```
78
+
79
+ This creates `rqgen.config.ts` in your project root with auto-detected settings:
80
+ - Reads your `tsconfig.json` / `tsconfig.app.json` to detect path aliases
81
+ - Detects if you have `src/`, `routes/`, `locales/` directories
82
+ - Asks about i18n, toast, and route registration preferences
83
+
84
+ ### 3. Generate Code
85
+
86
+ ```bash
87
+ # Interactive menu — pick from 12 generators
88
+ rq-codegen
89
+
90
+ # Or run a specific generator directly
91
+ rq-codegen handler
92
+ rq-codegen feature
93
+ rq-codegen page
94
+ ```
95
+
96
+ ---
97
+
98
+ ## CLI Commands
99
+
100
+ | Command | Description |
101
+ |---------|-------------|
102
+ | `rq-codegen` | Interactive menu — select a generator from the list |
103
+ | `rq-codegen <generator>` | Run a specific generator directly (e.g., `rq-codegen handler`) |
104
+ | `rq-codegen init` | Create `rqgen.config.ts` with auto-detected settings |
105
+ | `rq-codegen init --force` | Overwrite existing config file |
106
+ | `rq-codegen --version` | Show version |
107
+ | `rq-codegen --help` | Show help |
108
+
109
+ ### Interactive Menu
110
+
111
+ When you run `rq-codegen` without arguments, you get an interactive menu:
112
+
113
+ ```
114
+ ? What would you like to generate?
115
+ component-ui — CVA-based UI component (shadcn/ui style)
116
+ component-shared — Compound shared component
117
+ component-form — React Hook Form component (useController-based)
118
+ page — Route-level page component
119
+ view — Feature view component
120
+ handler — API handler (+ types + hooks)
121
+ query-hook — React Query hook
122
+ mutation-hook — React Query mutation hook
123
+ types-dto — DTO type definitions
124
+ shared-hook — Custom utility hook
125
+ validation — Zod validation schema
126
+ feature — Full feature scaffold (handler + types + hooks + view + page)
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Generators
132
+
133
+ ### handler
134
+
135
+ The most powerful single generator. Creates an API handler with typed request functions, and optionally chains DTO types, query hooks, and mutation hooks.
136
+
137
+ **Prompts:**
138
+
139
+ | Prompt | Example | Description |
140
+ |--------|---------|-------------|
141
+ | Entity name | `products` | Plural name for the handler (used for file names, handler object) |
142
+ | Singular name | `product` | Used for mutation hook names (e.g., `useCreateProductMutation`) |
143
+ | Endpoint key | `PRODUCTS` | ApiEndpoints constant key (e.g., `ApiEndpoints.PRODUCTS`) |
144
+ | Operations | `list, details, create, update, delete` | Which CRUD operations to generate |
145
+ | Also generate DTO types? | `Yes` | Chains the `types-dto` generator |
146
+ | Also generate query hooks? | `Yes` | Chains the `query-hook` generator |
147
+ | Is list paginated? | `No` | Uses paginated query hook variant |
148
+ | Also generate mutation hooks? | `Yes` | Chains the `mutation-hook` generator |
149
+
150
+ **Example:** `rq-codegen handler` with entity `products`, all operations, all chains enabled:
151
+
152
+ ```
153
+ CREATED src/api/handlers/products.ts
154
+ UPDATED src/api/handlers/index.ts
155
+ CREATED src/types/api/ProductsDto.ts
156
+ UPDATED src/types/api/index.ts
157
+ CREATED src/lib/hooks/queries/useProductsListQuery.ts
158
+ UPDATED src/lib/hooks/queries/index.ts
159
+ CREATED src/lib/hooks/queries/useProductsDetailsQuery.ts
160
+ UPDATED src/lib/hooks/queries/index.ts
161
+ CREATED src/lib/hooks/mutations/useCreateProductMutation.ts
162
+ UPDATED src/lib/hooks/mutations/index.ts
163
+ CREATED src/lib/hooks/mutations/useUpdateProductMutation.ts
164
+ UPDATED src/lib/hooks/mutations/index.ts
165
+ CREATED src/lib/hooks/mutations/useDeleteProductMutation.ts
166
+ UPDATED src/lib/hooks/mutations/index.ts
167
+
168
+ Done! 7 file(s) created, 6 barrel(s) updated.
169
+ ```
170
+
171
+ **Generated handler (`products.ts`):**
172
+
173
+ ```typescript
174
+ import { ApiEndpoints, HttpClient } from '@api/config';
175
+
176
+ import type {
177
+ ProductsForReadDto,
178
+ ProductsListResponseDto,
179
+ ProductsForCreateDto,
180
+ ProductsForUpdateDto,
181
+ } from '@app-types';
182
+
183
+ const URL = ApiEndpoints.PRODUCTS;
184
+
185
+ function getProductsList(queryString?: string): Promise<ProductsListResponseDto> {
186
+ const url = queryString ? `${URL.INDEX}?${queryString}` : URL.INDEX;
187
+ return HttpClient.get<ProductsListResponseDto>(url);
188
+ }
189
+
190
+ function getProductsDetails(id: string): Promise<ProductsForReadDto> {
191
+ return HttpClient.get<ProductsForReadDto>(URL.DETAILS.replace(':id', id));
192
+ }
193
+
194
+ function createProducts(payload: ProductsForCreateDto): Promise<ProductsForReadDto> {
195
+ return HttpClient.post<ProductsForReadDto>(URL.INDEX, payload);
196
+ }
197
+
198
+ function updateProducts(id: string, payload: ProductsForUpdateDto): Promise<ProductsForReadDto> {
199
+ return HttpClient.put<ProductsForReadDto>(URL.DETAILS.replace(':id', id), payload);
200
+ }
201
+
202
+ function removeProducts(id: string): Promise<void> {
203
+ return HttpClient.delete<void>(URL.DETAILS.replace(':id', id));
204
+ }
205
+
206
+ export const ProductsHandler = {
207
+ list: {
208
+ queryKey: 'products/list',
209
+ request: getProductsList,
210
+ },
211
+ details: {
212
+ queryKey: 'products/details',
213
+ request: getProductsDetails,
214
+ },
215
+ create: {
216
+ mutationKey: 'products/create',
217
+ mutationFn: createProducts,
218
+ },
219
+ update: {
220
+ mutationKey: 'products/update',
221
+ mutationFn: updateProducts,
222
+ },
223
+ remove: {
224
+ mutationKey: 'products/remove',
225
+ mutationFn: removeProducts,
226
+ },
227
+ } as const;
228
+ ```
229
+
230
+ ---
231
+
232
+ ### types-dto
233
+
234
+ Generates DTO type definitions for an API entity.
235
+
236
+ **Prompts:**
237
+
238
+ | Prompt | Example | Description |
239
+ |--------|---------|-------------|
240
+ | Entity name | `products` | Name used for type prefixes |
241
+ | Include create DTO? | `Yes` | Adds `ForCreateDto` type |
242
+ | Include update DTO? | `Yes` | Adds `ForUpdateDto` type |
243
+
244
+ **Generated types (`ProductsDto.ts`):**
245
+
246
+ ```typescript
247
+ export type ProductsForReadDto = {
248
+ id: number;
249
+ // TODO: Add read fields
250
+ };
251
+
252
+ export type ProductsForCreateDto = {
253
+ // TODO: Add create fields
254
+ };
255
+
256
+ export type ProductsForUpdateDto = {
257
+ id: number;
258
+ // TODO: Add update fields
259
+ };
260
+
261
+ export type ProductsListDto = {
262
+ id: number;
263
+ // TODO: Add minimal list fields
264
+ };
265
+
266
+ export type ProductsListResponseDto = {
267
+ items: ProductsListDto[];
268
+ page: number;
269
+ pageSize: number;
270
+ totalCount: number;
271
+ lastPage: number;
272
+ };
273
+
274
+ export type ProductsParamsDto = {
275
+ page?: number;
276
+ pageSize?: number;
277
+ search?: string;
278
+ sort?: string;
279
+ filter?: string;
280
+ };
281
+ ```
282
+
283
+ The DTO suffix names (`ForReadDto`, `ForCreateDto`, etc.) are fully configurable via `naming.dtoSuffixes` in your config.
284
+
285
+ ---
286
+
287
+ ### query-hook
288
+
289
+ Generates a React Query hook that wraps a handler.
290
+
291
+ **Prompts:**
292
+
293
+ | Prompt | Example | Description |
294
+ |--------|---------|-------------|
295
+ | Hook name | `productsList` | Used in `useProductsListQuery` |
296
+ | Handler name | `products` | References the handler (e.g., `ProductsHandler`) |
297
+ | Handler key | `list` | Handler method key (e.g., `ProductsHandler.list`) |
298
+ | Is paginated? | `No` | Uses paginated hook variant |
299
+
300
+ **Generated query hook (standard):**
301
+
302
+ ```typescript
303
+ import { ProductsHandler } from '@api/handlers';
304
+ import { useQuery } from '@tanstack/react-query';
305
+
306
+ export const useProductsListQuery = (enabled = true) => {
307
+ return useQuery({
308
+ queryKey: [ProductsHandler.list.queryKey],
309
+ queryFn: () => ProductsHandler.list.request(),
310
+ enabled,
311
+ staleTime: 5 * 60 * 1000,
312
+ gcTime: 10 * 60 * 1000,
313
+ });
314
+ };
315
+ ```
316
+
317
+ **Generated query hook (details variant):**
318
+
319
+ ```typescript
320
+ import { ProductsHandler } from '@api/handlers';
321
+ import { useQuery } from '@tanstack/react-query';
322
+
323
+ export const useProductsDetailsQuery = (id: string | undefined, enabled = true) => {
324
+ return useQuery({
325
+ queryKey: [ProductsHandler.details.queryKey, id],
326
+ queryFn: () => ProductsHandler.details.request(id!),
327
+ enabled: enabled && !!id,
328
+ staleTime: 5 * 60 * 1000,
329
+ gcTime: 10 * 60 * 1000,
330
+ });
331
+ };
332
+ ```
333
+
334
+ **Generated query hook (paginated variant):**
335
+
336
+ ```typescript
337
+ import { ProductsHandler } from '@api/handlers';
338
+ import { usePaginatedDataTableQuery } from '@hooks/utils';
339
+
340
+ import type { ProductsListDto } from '@app-types';
341
+
342
+ export const useProductsPaginatedQuery = () => {
343
+ return usePaginatedDataTableQuery<ProductsListDto>({
344
+ queryKey: [ProductsHandler.list.queryKey],
345
+ queryFn: (params: string) => ProductsHandler.list.request(params),
346
+ defaultPageSize: 10,
347
+ });
348
+ };
349
+ ```
350
+
351
+ ---
352
+
353
+ ### mutation-hook
354
+
355
+ Generates a React Query mutation hook with automatic query invalidation, toast notifications, and i18n support.
356
+
357
+ **Prompts:**
358
+
359
+ | Prompt | Example | Description |
360
+ |--------|---------|-------------|
361
+ | Mutation name | `CreateProduct` | Used in `useCreateProductMutation` |
362
+ | Handler name | `products` | References the handler |
363
+ | Handler key | `create` | Handler method key |
364
+ | Invalidate key | `list` | Query key to invalidate on success |
365
+
366
+ **Generated mutation hook (with i18n + toast enabled):**
367
+
368
+ ```typescript
369
+ import { ProductsHandler } from '@api/handlers';
370
+ import { useToast } from '@hooks/shared';
371
+ import { useAppTranslation } from '@hooks/shared';
372
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
373
+
374
+ export const useCreateProductMutation = () => {
375
+ const queryClient = useQueryClient();
376
+ const { toast } = useToast();
377
+ const { t } = useAppTranslation();
378
+
379
+ return useMutation({
380
+ mutationKey: [ProductsHandler.create.mutationKey],
381
+ mutationFn: ProductsHandler.create.mutationFn,
382
+ onSuccess: () => {
383
+ queryClient.invalidateQueries({
384
+ queryKey: [ProductsHandler.list.queryKey],
385
+ });
386
+ toast({
387
+ variant: 'success',
388
+ title: t('common.success'),
389
+ });
390
+ },
391
+ onError: (error) => {
392
+ const errorMessage =
393
+ error?.response?.data?.title || error?.response?.data?.message || t('errors.unexpectedError');
394
+ toast({
395
+ variant: 'destructive',
396
+ title: errorMessage,
397
+ description: error?.response?.data?.detail || '',
398
+ });
399
+ },
400
+ });
401
+ };
402
+ ```
403
+
404
+ When `features.toast` or `features.i18n` is disabled, the corresponding import and usage lines are omitted automatically.
405
+
406
+ ---
407
+
408
+ ### component-ui
409
+
410
+ Generates a CVA-based UI component (shadcn/ui style) with variants and barrel export.
411
+
412
+ **Prompts:**
413
+
414
+ | Prompt | Example | Description |
415
+ |--------|---------|-------------|
416
+ | Component name | `StatusBadge` | PascalCase component name |
417
+
418
+ **Generated files:**
419
+
420
+ ```
421
+ src/components/ui/status-badge/StatusBadge.tsx
422
+ src/components/ui/status-badge/index.ts
423
+ ```
424
+
425
+ **Generated component:**
426
+
427
+ ```typescript
428
+ import * as React from 'react';
429
+ import { cva, type VariantProps } from 'class-variance-authority';
430
+ import { cn } from '@utils';
431
+
432
+ export const statusBadgeVariants = cva(
433
+ 'inline-flex items-center justify-center',
434
+ {
435
+ variants: {
436
+ variant: {
437
+ default: '',
438
+ },
439
+ size: {
440
+ sm: '',
441
+ md: '',
442
+ lg: '',
443
+ },
444
+ },
445
+ defaultVariants: {
446
+ variant: 'default',
447
+ size: 'md',
448
+ },
449
+ },
450
+ );
451
+
452
+ export type StatusBadgeVariants = VariantProps<typeof statusBadgeVariants>;
453
+
454
+ type StatusBadgeProps = React.ComponentPropsWithoutRef<'div'> & StatusBadgeVariants;
455
+
456
+ export default function StatusBadge({ className, variant, size, children, ...props }: StatusBadgeProps) {
457
+ return (
458
+ <div
459
+ data-slot="status-badge"
460
+ className={cn(statusBadgeVariants({ variant, size, className }))}
461
+ {...props}
462
+ >
463
+ {children}
464
+ </div>
465
+ );
466
+ }
467
+ ```
468
+
469
+ ---
470
+
471
+ ### component-shared
472
+
473
+ Generates a compound shared component with configurable sub-components.
474
+
475
+ **Prompts:**
476
+
477
+ | Prompt | Example | Description |
478
+ |--------|---------|-------------|
479
+ | Component name | `InfoCard` | PascalCase component name |
480
+ | Sub-components | `Header, Body, Footer` | Comma-separated sub-component names |
481
+
482
+ **Generated files:**
483
+
484
+ ```
485
+ src/components/shared/info-card/InfoCard.tsx
486
+ src/components/shared/info-card/index.ts
487
+ ```
488
+
489
+ **Generated component:**
490
+
491
+ ```typescript
492
+ import { type FC, type ReactNode } from 'react';
493
+ import { cn } from '@utils';
494
+
495
+ type HeaderProps = {
496
+ children: ReactNode;
497
+ className?: string;
498
+ };
499
+
500
+ type BodyProps = {
501
+ children: ReactNode;
502
+ className?: string;
503
+ };
504
+
505
+ type InfoCardProps = {
506
+ children: ReactNode;
507
+ className?: string;
508
+ };
509
+
510
+ type InfoCardComponent = FC<InfoCardProps> & {
511
+ Header: FC<HeaderProps>;
512
+ Body: FC<BodyProps>;
513
+ };
514
+
515
+ const Header: FC<HeaderProps> = ({ children, className }) => (
516
+ <div className={cn('', className)}>{children}</div>
517
+ );
518
+
519
+ const Body: FC<BodyProps> = ({ children, className }) => (
520
+ <div className={cn('', className)}>{children}</div>
521
+ );
522
+
523
+ const InfoCard: InfoCardComponent = ({ children, className }) => (
524
+ <div className={cn('rounded-lg border bg-card', className)}>{children}</div>
525
+ );
526
+
527
+ InfoCard.Header = Header;
528
+ InfoCard.Body = Body;
529
+
530
+ export default InfoCard;
531
+ ```
532
+
533
+ **Usage:**
534
+
535
+ ```tsx
536
+ <InfoCard>
537
+ <InfoCard.Header>Title</InfoCard.Header>
538
+ <InfoCard.Body>Content</InfoCard.Body>
539
+ </InfoCard>
540
+ ```
541
+
542
+ ---
543
+
544
+ ### component-form
545
+
546
+ Generates a React Hook Form `useController`-based form field component.
547
+
548
+ **Prompts:**
549
+
550
+ | Prompt | Example | Description |
551
+ |--------|---------|-------------|
552
+ | Component name | `PhoneInput` | PascalCase component name |
553
+
554
+ **Generated files:**
555
+
556
+ ```
557
+ src/components/forms/form-phone-input/FormPhoneInput.tsx
558
+ src/components/forms/form-phone-input/index.ts
559
+ ```
560
+
561
+ ---
562
+
563
+ ### page
564
+
565
+ Generates a route-level page component. Optionally auto-registers the route in your router.
566
+
567
+ **Prompts:**
568
+
569
+ | Prompt | Example | Description |
570
+ |--------|---------|-------------|
571
+ | Page name | `Dashboard` | PascalCase page name |
572
+ | Category | `admin` or `Create new category` | Folder grouping |
573
+ | Register route? | `Yes` | (Only when `routeRegistration` enabled) |
574
+ | Layout | `DashboardLayout` | (Only when registering route) |
575
+ | Is protected? | `Yes` | (Only when registering route) |
576
+ | Route path | `/admin/dashboard` | (Only when registering route) |
577
+
578
+ **Generated file:**
579
+
580
+ ```
581
+ src/pages/admin/DashboardPage.tsx
582
+ ```
583
+
584
+ **Generated component:**
585
+
586
+ ```typescript
587
+ export default function DashboardPage() {
588
+ return (
589
+ <div>
590
+ <h1>Dashboard</h1>
591
+ </div>
592
+ );
593
+ }
594
+ ```
595
+
596
+ The page suffix (`Page`) is configurable via `naming.pageSuffix`.
597
+
598
+ ---
599
+
600
+ ### view
601
+
602
+ Generates a feature view component with barrel exports.
603
+
604
+ **Prompts:**
605
+
606
+ | Prompt | Example | Description |
607
+ |--------|---------|-------------|
608
+ | View name | `ProductCard` | PascalCase view name |
609
+ | Category | `products` or `Create new category` | Feature folder grouping |
610
+
611
+ **Generated files:**
612
+
613
+ ```
614
+ src/views/products/product-card/ProductCard.tsx
615
+ src/views/products/product-card/index.ts
616
+ ```
617
+
618
+ ---
619
+
620
+ ### shared-hook
621
+
622
+ Generates a custom utility hook.
623
+
624
+ **Prompts:**
625
+
626
+ | Prompt | Example | Description |
627
+ |--------|---------|-------------|
628
+ | Hook name | `windowSize` | camelCase name (generates `useWindowSize`) |
629
+ | React imports | `useState, useEffect` | Selected from checkbox |
630
+
631
+ **Generated file:**
632
+
633
+ ```
634
+ src/lib/hooks/shared/useWindowSize.ts
635
+ ```
636
+
637
+ **Generated hook:**
638
+
639
+ ```typescript
640
+ import { useState, useEffect } from 'react';
641
+
642
+ export const useWindowSize = () => {
643
+ // TODO: Implement hook logic
644
+
645
+ return {
646
+ // TODO: Return hook values
647
+ };
648
+ };
649
+ ```
650
+
651
+ ---
652
+
653
+ ### validation
654
+
655
+ Generates a Zod validation schema with optional i18n support.
656
+
657
+ **Prompts:**
658
+
659
+ | Prompt | Example | Description |
660
+ |--------|---------|-------------|
661
+ | Schema name | `product` | camelCase name |
662
+
663
+ **Generated file:**
664
+
665
+ ```
666
+ src/validations/product.schema.ts
667
+ ```
668
+
669
+ **Generated schema (with i18n enabled):**
670
+
671
+ ```typescript
672
+ import { z } from 'zod';
673
+ import type { TFunction } from 'i18next';
674
+
675
+ export const productSchema = (t: TFunction) =>
676
+ z.object({
677
+ // TODO: Add validation fields
678
+ // Example:
679
+ // name: z.string().min(1, t('validation.product.name.required')),
680
+ });
681
+
682
+ export type ProductFormData = z.infer<ReturnType<typeof productSchema>>;
683
+ ```
684
+
685
+ **Generated schema (without i18n):**
686
+
687
+ ```typescript
688
+ import { z } from 'zod';
689
+
690
+ export const productSchema = z.object({
691
+ // TODO: Add validation fields
692
+ // Example:
693
+ // name: z.string().min(1, 'Name is required'),
694
+ });
695
+
696
+ export type ProductFormData = z.infer<typeof productSchema>;
697
+ ```
698
+
699
+ The validation file suffix (`.schema.ts`) is configurable via `naming.validationSuffix`.
700
+
701
+ ---
702
+
703
+ ### feature
704
+
705
+ The composite generator. Scaffolds an entire feature in one command — selecting which artifacts to generate.
706
+
707
+ **Prompts:**
708
+
709
+ | Prompt | Example | Description |
710
+ |--------|---------|-------------|
711
+ | Feature name | `products` | Plural entity name |
712
+ | Singular name | `product` | For mutation naming |
713
+ | Endpoint key | `PRODUCTS` | ApiEndpoints constant |
714
+ | Artifacts | (checkbox) | Select what to generate |
715
+ | Is paginated? | `No` | (When query list selected) |
716
+
717
+ **Available artifacts:**
718
+
719
+ - API Handler
720
+ - DTO Types
721
+ - Query Hook (list)
722
+ - Query Hook (details)
723
+ - Mutation Hook (create)
724
+ - Mutation Hook (update)
725
+ - Mutation Hook (delete)
726
+ - View Component
727
+ - Page Component
728
+ - Validation Schema
729
+
730
+ **Example:** Running `rq-codegen feature` with `products`, all artifacts selected:
731
+
732
+ ```
733
+ CREATED src/types/api/ProductsDto.ts
734
+ UPDATED src/types/api/index.ts
735
+ CREATED src/api/handlers/products.ts
736
+ UPDATED src/api/handlers/index.ts
737
+ CREATED src/lib/hooks/queries/useProductsListQuery.ts
738
+ UPDATED src/lib/hooks/queries/index.ts
739
+ CREATED src/lib/hooks/queries/useProductsDetailsQuery.ts
740
+ UPDATED src/lib/hooks/queries/index.ts
741
+ CREATED src/lib/hooks/mutations/useCreateProductMutation.ts
742
+ UPDATED src/lib/hooks/mutations/index.ts
743
+ CREATED src/lib/hooks/mutations/useUpdateProductMutation.ts
744
+ UPDATED src/lib/hooks/mutations/index.ts
745
+ CREATED src/lib/hooks/mutations/useDeleteProductMutation.ts
746
+ UPDATED src/lib/hooks/mutations/index.ts
747
+ CREATED src/views/products/products-list/ProductsList.tsx
748
+ CREATED src/views/products/products-list/index.ts
749
+ UPDATED src/views/products/index.ts
750
+ UPDATED src/views/index.ts
751
+ CREATED src/pages/products/ProductsPage.tsx
752
+ CREATED src/validations/products.schema.ts
753
+ UPDATED src/validations/index.ts
754
+
755
+ Done! 11 file(s) created, 10 barrel(s) updated.
756
+ ```
757
+
758
+ ---
759
+
760
+ ## Configuration Reference
761
+
762
+ Configuration is loaded from `rqgen.config.ts` (or `.js`, `.mjs`, `.mts`) in your project root, or from a `"rqgen"` field in `package.json`.
763
+
764
+ ### Full Config Example
765
+
766
+ ```typescript
767
+ // rqgen.config.ts
768
+ import { defineConfig } from '@appswave/rq-codegen';
769
+
770
+ export default defineConfig({
771
+ srcDir: './src',
772
+
773
+ aliases: {
774
+ api: '@api',
775
+ components: '@components',
776
+ hooks: '@hooks',
777
+ types: '@app-types',
778
+ utils: '@utils',
779
+ contexts: '@contexts',
780
+ constants: '@constants',
781
+ views: '@views',
782
+ pages: '@pages',
783
+ validations: '@validations',
784
+ assets: '@assets',
785
+ routes: '@routes',
786
+ hoc: '@hoc',
787
+ appConfig: '@app-config',
788
+ },
789
+
790
+ features: {
791
+ i18n: true,
792
+ toast: true,
793
+ barrel: true,
794
+ routeRegistration: true,
795
+ },
796
+
797
+ naming: {
798
+ dtoSuffixes: {
799
+ read: 'ForReadDto',
800
+ create: 'ForCreateDto',
801
+ update: 'ForUpdateDto',
802
+ list: 'ListDto',
803
+ listResponse: 'ListResponseDto',
804
+ params: 'ParamsDto',
805
+ },
806
+ validationSuffix: '.schema.ts',
807
+ pageSuffix: 'Page',
808
+ hookPrefix: 'use',
809
+ },
810
+
811
+ paths: {
812
+ handlers: 'api/handlers',
813
+ apiConfig: 'api/config',
814
+ types: 'types/api',
815
+ queries: 'lib/hooks/queries',
816
+ mutations: 'lib/hooks/mutations',
817
+ sharedHooks: 'lib/hooks/shared',
818
+ hookUtils: 'lib/hooks/utils',
819
+ uiComponents: 'components/ui',
820
+ sharedComponents: 'components/shared',
821
+ formComponents: 'components/forms',
822
+ pages: 'pages',
823
+ views: 'views',
824
+ validations: 'validations',
825
+ },
826
+
827
+ router: {
828
+ routerFile: 'routes/router.tsx',
829
+ routesFile: 'routes/routes.ts',
830
+ layouts: ['MainLayout', 'DashboardLayout'],
831
+ },
832
+
833
+ hooks: {
834
+ toast: { import: 'useToast', from: '@hooks/shared' },
835
+ translation: { import: 'useAppTranslation', from: '@hooks/shared' },
836
+ paginatedQuery: { import: 'usePaginatedDataTableQuery', from: '@hooks/utils' },
837
+ },
838
+
839
+ templatesDir: './my-templates',
840
+ });
841
+ ```
842
+
843
+ ### srcDir
844
+
845
+ **Type:** `string`
846
+ **Default:** `'./src'`
847
+
848
+ Root source directory. All `paths` are relative to this directory.
849
+
850
+ ```typescript
851
+ srcDir: './src',
852
+ ```
853
+
854
+ ### aliases
855
+
856
+ **Type:** `AliasConfig`
857
+
858
+ Path aliases used in import statements of generated files. These should match your `tsconfig.json` `paths` configuration.
859
+
860
+ ```typescript
861
+ aliases: {
862
+ api: '@api', // Used in: import { HttpClient } from '@api/config'
863
+ components: '@components', // Used in: import { Button } from '@components/ui'
864
+ hooks: '@hooks', // Used in: import { useToast } from '@hooks/shared'
865
+ types: '@app-types', // Used in: import type { UserDto } from '@app-types'
866
+ utils: '@utils', // Used in: import { cn } from '@utils'
867
+ contexts: '@contexts',
868
+ constants: '@constants',
869
+ views: '@views',
870
+ pages: '@pages',
871
+ validations: '@validations',
872
+ assets: '@assets',
873
+ routes: '@routes',
874
+ hoc: '@hoc',
875
+ appConfig: '@app-config',
876
+ },
877
+ ```
878
+
879
+ **Tip:** `rq-codegen init` auto-detects aliases from your `tsconfig.json` or `tsconfig.app.json`.
880
+
881
+ ### features
882
+
883
+ **Type:** `FeatureToggles`
884
+
885
+ Toggle features on/off to control what gets generated. See [Feature Toggles](#feature-toggles) for details.
886
+
887
+ ```typescript
888
+ features: {
889
+ i18n: true, // Include i18n imports in mutations and validations
890
+ toast: true, // Include toast notifications in mutations
891
+ barrel: true, // Auto-update barrel (index.ts) exports
892
+ routeRegistration: true, // Enable route auto-registration for pages
893
+ },
894
+ ```
895
+
896
+ ### naming
897
+
898
+ **Type:** `NamingConfig`
899
+
900
+ Customize naming conventions for generated files and types.
901
+
902
+ ```typescript
903
+ naming: {
904
+ dtoSuffixes: {
905
+ read: 'ForReadDto', // e.g., ProductsForReadDto
906
+ create: 'ForCreateDto', // e.g., ProductsForCreateDto
907
+ update: 'ForUpdateDto', // e.g., ProductsForUpdateDto
908
+ list: 'ListDto', // e.g., ProductsListDto
909
+ listResponse: 'ListResponseDto', // e.g., ProductsListResponseDto
910
+ params: 'ParamsDto', // e.g., ProductsParamsDto
911
+ },
912
+ validationSuffix: '.schema.ts', // File suffix: product.schema.ts
913
+ pageSuffix: 'Page', // Component suffix: DashboardPage
914
+ hookPrefix: 'use', // Hook prefix: useProducts
915
+ },
916
+ ```
917
+
918
+ **Custom DTO suffixes example:**
919
+
920
+ ```typescript
921
+ naming: {
922
+ dtoSuffixes: {
923
+ read: 'Dto', // ProductsDto instead of ProductsForReadDto
924
+ create: 'CreateDto', // ProductsCreateDto instead of ProductsForCreateDto
925
+ },
926
+ },
927
+ ```
928
+
929
+ ### paths
930
+
931
+ **Type:** `PathsConfig`
932
+
933
+ Relative paths (from `srcDir`) where generated files are placed.
934
+
935
+ ```typescript
936
+ paths: {
937
+ handlers: 'api/handlers', // API handler files
938
+ apiConfig: 'api/config', // ApiEndpoints, HttpClient
939
+ types: 'types/api', // DTO type definitions
940
+ queries: 'lib/hooks/queries', // React Query hooks
941
+ mutations: 'lib/hooks/mutations', // Mutation hooks
942
+ sharedHooks: 'lib/hooks/shared', // Shared utility hooks
943
+ hookUtils: 'lib/hooks/utils', // Hook utilities (pagination, etc.)
944
+ uiComponents: 'components/ui', // UI primitives (shadcn/ui style)
945
+ sharedComponents: 'components/shared', // Compound shared components
946
+ formComponents: 'components/forms', // Form field components
947
+ pages: 'pages', // Route-level pages
948
+ views: 'views', // Feature view components
949
+ validations: 'validations', // Zod validation schemas
950
+ },
951
+ ```
952
+
953
+ ### router
954
+
955
+ **Type:** `RouterConfig`
956
+
957
+ Configuration for route auto-registration (used by the `page` generator when `features.routeRegistration` is enabled).
958
+
959
+ ```typescript
960
+ router: {
961
+ routerFile: 'routes/router.tsx', // File containing React Router config
962
+ routesFile: 'routes/routes.ts', // File containing route constants
963
+ layouts: ['MainLayout', 'DashboardLayout'], // Available layout options
964
+ },
965
+ ```
966
+
967
+ ### hooks
968
+
969
+ **Type:** `HooksConfig`
970
+
971
+ Configure import paths for hooks used in generated templates. This allows the generated code to import the exact hooks your project provides.
972
+
973
+ ```typescript
974
+ hooks: {
975
+ toast: {
976
+ import: 'useToast', // Hook function name
977
+ from: '@hooks/shared', // Import path
978
+ },
979
+ translation: {
980
+ import: 'useAppTranslation', // Hook function name
981
+ from: '@hooks/shared', // Import path
982
+ },
983
+ paginatedQuery: {
984
+ import: 'usePaginatedDataTableQuery', // Hook function name
985
+ from: '@hooks/utils', // Import path
986
+ },
987
+ },
988
+ ```
989
+
990
+ ### templatesDir
991
+
992
+ **Type:** `string | undefined`
993
+ **Default:** `undefined` (uses bundled templates)
994
+
995
+ Path to a local directory containing template overrides. Any `.hbs` file found in this directory takes precedence over the bundled template with the same relative path.
996
+
997
+ ```typescript
998
+ templatesDir: './my-templates',
999
+ ```
1000
+
1001
+ See [Template Overrides](#template-overrides) for details.
1002
+
1003
+ ---
1004
+
1005
+ ## Feature Toggles
1006
+
1007
+ ### `i18n`
1008
+
1009
+ When **enabled**, generated code includes:
1010
+ - `import type { TFunction } from 'i18next'` in validation schemas
1011
+ - Validation schemas accept a `t` function parameter for translated messages
1012
+ - `import { useAppTranslation } from '@hooks/shared'` in mutation hooks
1013
+ - Translated toast messages using `t('common.success')`
1014
+
1015
+ When **disabled**, all i18n-related imports and logic are omitted.
1016
+
1017
+ ### `toast`
1018
+
1019
+ When **enabled**, generated mutation hooks include:
1020
+ - `import { useToast } from '@hooks/shared'`
1021
+ - `onSuccess` toast notification
1022
+ - `onError` toast notification with error message extraction
1023
+
1024
+ When **disabled**, mutation hooks omit all toast-related code.
1025
+
1026
+ ### `barrel`
1027
+
1028
+ When **enabled**, every generator that creates a file also adds an `export * from './...'` line to the nearest `index.ts` barrel file. If the barrel file doesn't exist, it's created.
1029
+
1030
+ When **disabled**, barrel exports are skipped — you manage imports manually.
1031
+
1032
+ ### `routeRegistration`
1033
+
1034
+ When **enabled**, the `page` generator shows additional prompts:
1035
+ - Which layout to use
1036
+ - Whether the route is protected
1037
+ - The route URL path
1038
+
1039
+ And generates a `route-register` action that adds the lazy import and route entry to your router files.
1040
+
1041
+ When **disabled**, the `page` generator only creates the page component file.
1042
+
1043
+ ---
1044
+
1045
+ ## Template Overrides
1046
+
1047
+ Every generated file comes from a Handlebars template (`.hbs`). You can override any template by creating a local copy.
1048
+
1049
+ ### Steps
1050
+
1051
+ 1. Set `templatesDir` in your config:
1052
+
1053
+ ```typescript
1054
+ export default defineConfig({
1055
+ templatesDir: './templates',
1056
+ });
1057
+ ```
1058
+
1059
+ 2. Create the template file with the same relative path as the bundled template:
1060
+
1061
+ ```
1062
+ your-project/
1063
+ ├── templates/
1064
+ │ └── handler/
1065
+ │ └── handler.ts.hbs # Overrides the default handler template
1066
+ └── rqgen.config.ts
1067
+ ```
1068
+
1069
+ 3. The local template is used instead of the bundled one. All Handlebars helpers (`configAlias`, `dtoSuffix`, `ifFeature`, `pascalCase`, etc.) are available.
1070
+
1071
+ ### Available Templates
1072
+
1073
+ | Path | Description |
1074
+ |------|-------------|
1075
+ | `component-ui/Component.tsx.hbs` | CVA UI component |
1076
+ | `component-ui/index.ts.hbs` | UI component barrel |
1077
+ | `component-shared/Component.tsx.hbs` | Compound shared component |
1078
+ | `component-shared/index.ts.hbs` | Shared component barrel |
1079
+ | `component-form/FormComponent.tsx.hbs` | Form field component |
1080
+ | `component-form/index.ts.hbs` | Form component barrel |
1081
+ | `page/Page.tsx.hbs` | Page component |
1082
+ | `view/View.tsx.hbs` | View component |
1083
+ | `view/index.ts.hbs` | View barrel |
1084
+ | `handler/handler.ts.hbs` | API handler |
1085
+ | `query-hook/hook.ts.hbs` | Standard query hook |
1086
+ | `query-hook/hook-details.ts.hbs` | Details query hook |
1087
+ | `query-hook/hook-paginated.ts.hbs` | Paginated query hook |
1088
+ | `mutation-hook/hook.ts.hbs` | Mutation hook |
1089
+ | `types-dto/dto.ts.hbs` | DTO type definitions |
1090
+ | `shared-hook/hook.ts.hbs` | Shared utility hook |
1091
+ | `validation/validation.ts.hbs` | Zod validation schema |
1092
+
1093
+ ### Available Handlebars Helpers
1094
+
1095
+ | Helper | Usage | Description |
1096
+ |--------|-------|-------------|
1097
+ | `pascalCase` | `{{pascalCase name}}` | Converts to PascalCase |
1098
+ | `camelCase` | `{{camelCase name}}` | Converts to camelCase |
1099
+ | `kebabCase` | `{{kebabCase name}}` | Converts to kebab-case |
1100
+ | `constantCase` | `{{constantCase name}}` | Converts to CONSTANT_CASE |
1101
+ | `configAlias` | `{{configAlias "api"}}` | Returns alias from config (e.g., `@api`) |
1102
+ | `configPath` | `{{configPath "handlers"}}` | Returns path from config |
1103
+ | `dtoSuffix` | `{{dtoSuffix "read"}}` | Returns DTO suffix (e.g., `ForReadDto`) |
1104
+ | `ifFeature` | `{{#ifFeature "toast"}}...{{/ifFeature}}` | Conditional block based on feature toggle |
1105
+ | `plural` | `{{plural name}}` | Basic pluralization |
1106
+ | `eq` | `{{#if (eq a b)}}` | Equality check |
1107
+ | `neq` | `{{#if (neq a b)}}` | Inequality check |
1108
+ | `includes` | `{{#if (includes arr val)}}` | Array includes check |
1109
+ | `join` | `{{join arr ", "}}` | Join array with separator |
1110
+
1111
+ ---
1112
+
1113
+ ## Path Alias Detection
1114
+
1115
+ When you run `rq-codegen init`, aliases are auto-detected from your TypeScript config:
1116
+
1117
+ 1. Reads `tsconfig.json` in the current directory
1118
+ 2. If it has `"extends"`, follows to `tsconfig.app.json` (or whatever it extends)
1119
+ 3. Parses the `compilerOptions.paths` field
1120
+ 4. Maps known alias patterns to config keys:
1121
+
1122
+ | tsconfig paths | Config key | Default value |
1123
+ |---------------|------------|---------------|
1124
+ | `@api/*` | `aliases.api` | `@api` |
1125
+ | `@components/*` | `aliases.components` | `@components` |
1126
+ | `@hooks/*` | `aliases.hooks` | `@hooks` |
1127
+ | `@app-types` or `@types/*` | `aliases.types` | `@app-types` |
1128
+ | `@utils` | `aliases.utils` | `@utils` |
1129
+ | `@contexts` | `aliases.contexts` | `@contexts` |
1130
+ | `@constants` | `aliases.constants` | `@constants` |
1131
+ | `@views/*` | `aliases.views` | `@views` |
1132
+ | `@pages/*` | `aliases.pages` | `@pages` |
1133
+ | `@validations/*` | `aliases.validations` | `@validations` |
1134
+ | `@assets/*` | `aliases.assets` | `@assets` |
1135
+ | `@routes` | `aliases.routes` | `@routes` |
1136
+ | `@hoc` | `aliases.hoc` | `@hoc` |
1137
+ | `@app-config` | `aliases.appConfig` | `@app-config` |
1138
+
1139
+ ---
1140
+
1141
+ ## Recommended Project Structure
1142
+
1143
+ `rq-codegen` works best with this structure (all paths are configurable):
1144
+
1145
+ ```
1146
+ src/
1147
+ ├── api/
1148
+ │ ├── config/ # ApiEndpoints.ts, HttpClient.ts, etc.
1149
+ │ └── handlers/ # Generated API handlers + index.ts barrel
1150
+ ├── components/
1151
+ │ ├── ui/ # Generated UI components + index.ts barrel
1152
+ │ ├── shared/ # Generated shared components + index.ts barrel
1153
+ │ └── forms/ # Generated form components + index.ts barrel
1154
+ ├── lib/
1155
+ │ └── hooks/
1156
+ │ ├── queries/ # Generated query hooks + index.ts barrel
1157
+ │ ├── mutations/ # Generated mutation hooks + index.ts barrel
1158
+ │ ├── shared/ # Generated shared hooks + index.ts barrel
1159
+ │ └── utils/ # Paginated query hook, etc.
1160
+ ├── pages/ # Generated pages (grouped by category)
1161
+ ├── views/ # Generated views (grouped by feature)
1162
+ ├── types/
1163
+ │ └── api/ # Generated DTO types + index.ts barrel
1164
+ ├── validations/ # Generated Zod schemas + index.ts barrel
1165
+ └── routes/ # Router config (for auto-registration)
1166
+ ```
1167
+
1168
+ ---
1169
+
1170
+ ## defineConfig()
1171
+
1172
+ The `defineConfig()` helper provides type-safe configuration with IntelliSense:
1173
+
1174
+ ```typescript
1175
+ // rqgen.config.ts
1176
+ import { defineConfig } from '@appswave/rq-codegen';
1177
+
1178
+ export default defineConfig({
1179
+ // Full IntelliSense for all config options
1180
+ features: {
1181
+ i18n: true,
1182
+ },
1183
+ });
1184
+ ```
1185
+
1186
+ You only need to specify the fields you want to override — everything else uses sensible defaults.
1187
+
1188
+ ### Exported Types
1189
+
1190
+ ```typescript
1191
+ import type {
1192
+ RqCodegenConfig,
1193
+ AliasConfig,
1194
+ FeatureToggles,
1195
+ NamingConfig,
1196
+ PathsConfig,
1197
+ RouterConfig,
1198
+ HooksConfig,
1199
+ HookImportConfig,
1200
+ DtoSuffixes,
1201
+ } from '@appswave/rq-codegen';
1202
+ ```
1203
+
1204
+ ---
1205
+
1206
+ ## Contributing
1207
+
1208
+ ### Setup
1209
+
1210
+ ```bash
1211
+ git clone <repo-url>
1212
+ cd rq-codegen
1213
+ npm install
1214
+ ```
1215
+
1216
+ ### Development
1217
+
1218
+ ```bash
1219
+ # Build
1220
+ npm run build
1221
+
1222
+ # Watch mode
1223
+ npm run dev
1224
+
1225
+ # Type check
1226
+ npm run typecheck
1227
+
1228
+ # Run tests
1229
+ npm test
1230
+
1231
+ # Watch tests
1232
+ npm run test:watch
1233
+ ```
1234
+
1235
+ ### Local Testing
1236
+
1237
+ ```bash
1238
+ # Link globally
1239
+ npm link
1240
+
1241
+ # Use in any project
1242
+ cd /path/to/your/project
1243
+ rq-codegen init
1244
+ rq-codegen handler
1245
+ ```
1246
+
1247
+ ### Project Structure
1248
+
1249
+ ```
1250
+ rq-codegen/
1251
+ ├── bin/cli.ts # CLI entry point
1252
+ ├── src/
1253
+ │ ├── index.ts # Public API (defineConfig + types)
1254
+ │ ├── cli.ts # Commander setup
1255
+ │ ├── commands/
1256
+ │ │ ├── generate.ts # rq-codegen [generator]
1257
+ │ │ └── init.ts # rq-codegen init
1258
+ │ ├── config/
1259
+ │ │ ├── types.ts # RqCodegenConfig type
1260
+ │ │ ├── schema.ts # Zod validation
1261
+ │ │ ├── defaults.ts # Default values
1262
+ │ │ └── loader.ts # Config discovery + merge
1263
+ │ ├── core/
1264
+ │ │ ├── engine.ts # Handlebars engine + action executor
1265
+ │ │ ├── helpers.ts # 13 Handlebars helpers
1266
+ │ │ ├── actions.ts # barrel-append + route-register
1267
+ │ │ └── template-resolver.ts # Local override fallback
1268
+ │ ├── generators/ # 12 generators
1269
+ │ └── utils/ # String, validation, filesystem utilities
1270
+ ├── templates/ # 17 bundled Handlebars templates
1271
+ ├── tsup.config.ts # Build config (ESM)
1272
+ └── vitest.config.ts # Test config
1273
+ ```
1274
+
1275
+ ---
1276
+
1277
+ ## License
1278
+
1279
+ MIT