nuxt-openapi-hyperfetch 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/.editorconfig +26 -0
  2. package/.prettierignore +17 -0
  3. package/.prettierrc.json +12 -0
  4. package/CONTRIBUTING.md +292 -0
  5. package/INSTRUCTIONS.md +327 -0
  6. package/LICENSE +202 -0
  7. package/README.md +202 -0
  8. package/dist/cli/config.d.ts +57 -0
  9. package/dist/cli/config.js +85 -0
  10. package/dist/cli/logger.d.ts +44 -0
  11. package/dist/cli/logger.js +58 -0
  12. package/dist/cli/logo.d.ts +6 -0
  13. package/dist/cli/logo.js +21 -0
  14. package/dist/cli/messages.d.ts +65 -0
  15. package/dist/cli/messages.js +86 -0
  16. package/dist/cli/prompts.d.ts +30 -0
  17. package/dist/cli/prompts.js +118 -0
  18. package/dist/cli/types.d.ts +43 -0
  19. package/dist/cli/types.js +4 -0
  20. package/dist/cli/utils.d.ts +26 -0
  21. package/dist/cli/utils.js +45 -0
  22. package/dist/generate.d.ts +6 -0
  23. package/dist/generate.js +48 -0
  24. package/dist/generators/nuxt-server/bff-templates.d.ts +25 -0
  25. package/dist/generators/nuxt-server/bff-templates.js +737 -0
  26. package/dist/generators/nuxt-server/generator.d.ts +7 -0
  27. package/dist/generators/nuxt-server/generator.js +206 -0
  28. package/dist/generators/nuxt-server/parser.d.ts +5 -0
  29. package/dist/generators/nuxt-server/parser.js +5 -0
  30. package/dist/generators/nuxt-server/templates.d.ts +35 -0
  31. package/dist/generators/nuxt-server/templates.js +412 -0
  32. package/dist/generators/nuxt-server/types.d.ts +5 -0
  33. package/dist/generators/nuxt-server/types.js +5 -0
  34. package/dist/generators/shared/parsers/heyapi-parser.d.ts +11 -0
  35. package/dist/generators/shared/parsers/heyapi-parser.js +248 -0
  36. package/dist/generators/shared/parsers/official-parser.d.ts +5 -0
  37. package/dist/generators/shared/parsers/official-parser.js +5 -0
  38. package/dist/generators/shared/runtime/apiHelpers.d.ts +183 -0
  39. package/dist/generators/shared/runtime/apiHelpers.js +268 -0
  40. package/dist/generators/shared/templates/api-callbacks-plugin.d.ts +178 -0
  41. package/dist/generators/shared/templates/api-callbacks-plugin.js +338 -0
  42. package/dist/generators/shared/types.d.ts +25 -0
  43. package/dist/generators/shared/types.js +4 -0
  44. package/dist/generators/tanstack-query/generator.d.ts +5 -0
  45. package/dist/generators/tanstack-query/generator.js +11 -0
  46. package/dist/generators/use-async-data/generator.d.ts +5 -0
  47. package/dist/generators/use-async-data/generator.js +156 -0
  48. package/dist/generators/use-async-data/parser.d.ts +5 -0
  49. package/dist/generators/use-async-data/parser.js +5 -0
  50. package/dist/generators/use-async-data/runtime/useApiAsyncData.d.ts +38 -0
  51. package/dist/generators/use-async-data/runtime/useApiAsyncData.js +122 -0
  52. package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.d.ts +54 -0
  53. package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +126 -0
  54. package/dist/generators/use-async-data/templates.d.ts +20 -0
  55. package/dist/generators/use-async-data/templates.js +191 -0
  56. package/dist/generators/use-async-data/types.d.ts +4 -0
  57. package/dist/generators/use-async-data/types.js +4 -0
  58. package/dist/generators/use-fetch/generator.d.ts +5 -0
  59. package/dist/generators/use-fetch/generator.js +131 -0
  60. package/dist/generators/use-fetch/parser.d.ts +9 -0
  61. package/dist/generators/use-fetch/parser.js +282 -0
  62. package/dist/generators/use-fetch/runtime/useApiRequest.d.ts +46 -0
  63. package/dist/generators/use-fetch/runtime/useApiRequest.js +158 -0
  64. package/dist/generators/use-fetch/templates.d.ts +16 -0
  65. package/dist/generators/use-fetch/templates.js +169 -0
  66. package/dist/generators/use-fetch/types.d.ts +5 -0
  67. package/dist/generators/use-fetch/types.js +5 -0
  68. package/dist/index.d.ts +2 -0
  69. package/dist/index.js +213 -0
  70. package/docs/API-REFERENCE.md +887 -0
  71. package/docs/ARCHITECTURE.md +649 -0
  72. package/docs/DEVELOPMENT.md +918 -0
  73. package/docs/QUICK-START.md +323 -0
  74. package/docs/README.md +155 -0
  75. package/docs/TROUBLESHOOTING.md +881 -0
  76. package/eslint.config.js +72 -0
  77. package/package.json +65 -0
  78. package/src/cli/config.ts +140 -0
  79. package/src/cli/logger.ts +66 -0
  80. package/src/cli/logo.ts +25 -0
  81. package/src/cli/messages.ts +97 -0
  82. package/src/cli/prompts.ts +143 -0
  83. package/src/cli/types.ts +50 -0
  84. package/src/cli/utils.ts +49 -0
  85. package/src/generate.ts +57 -0
  86. package/src/generators/nuxt-server/bff-templates.ts +754 -0
  87. package/src/generators/nuxt-server/generator.ts +270 -0
  88. package/src/generators/nuxt-server/parser.ts +5 -0
  89. package/src/generators/nuxt-server/templates.ts +483 -0
  90. package/src/generators/nuxt-server/types.ts +5 -0
  91. package/src/generators/shared/parsers/heyapi-parser.ts +307 -0
  92. package/src/generators/shared/parsers/official-parser.ts +5 -0
  93. package/src/generators/shared/runtime/apiHelpers.ts +466 -0
  94. package/src/generators/shared/templates/api-callbacks-plugin.ts +352 -0
  95. package/src/generators/shared/types.ts +27 -0
  96. package/src/generators/tanstack-query/generator.ts +11 -0
  97. package/src/generators/use-async-data/generator.ts +204 -0
  98. package/src/generators/use-async-data/parser.ts +5 -0
  99. package/src/generators/use-async-data/runtime/useApiAsyncData.ts +220 -0
  100. package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +236 -0
  101. package/src/generators/use-async-data/templates.ts +250 -0
  102. package/src/generators/use-async-data/types.ts +4 -0
  103. package/src/generators/use-fetch/generator.ts +169 -0
  104. package/src/generators/use-fetch/parser.ts +341 -0
  105. package/src/generators/use-fetch/runtime/useApiRequest.ts +223 -0
  106. package/src/generators/use-fetch/templates.ts +214 -0
  107. package/src/generators/use-fetch/types.ts +5 -0
  108. package/src/index.ts +265 -0
  109. package/tsconfig.json +15 -0
@@ -0,0 +1,918 @@
1
+ # 🔧 Development Guide - Contributing & Extending
2
+
3
+ > **Purpose**: Practical guide for developers who want to contribute, extend, or customize the generator.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Getting Started](#getting-started)
8
+ - [Development Workflow](#development-workflow)
9
+ - [Adding Features](#adding-features)
10
+ - [Testing Changes](#testing-changes)
11
+ - [Code Style](#code-style)
12
+ - [Common Tasks](#common-tasks)
13
+ - [Extension Points](#extension-points)
14
+ - [Debugging Guide](#debugging-guide)
15
+
16
+ ## Getting Started
17
+
18
+ ### Prerequisites
19
+
20
+ - **Node.js**: 18.x or higher
21
+ - **npm**: 9.x or higher
22
+ - **TypeScript**: Basic knowledge
23
+ - **Git**: For version control
24
+
25
+ ### Initial Setup
26
+
27
+ ```bash
28
+ # Clone repository
29
+ git clone https://github.com/dmartindiaz/nuxt-openapi-hyperfetch.git
30
+ cd nuxt-openapi-hyperfetch
31
+
32
+ # Install dependencies
33
+ npm install
34
+
35
+ # Build project
36
+ npm run build
37
+
38
+ # Test generation
39
+ npm run generator
40
+
41
+ # Or with parameters
42
+ echo "useFetch" | npm run generator
43
+ ```
44
+
45
+ ### Project Scripts
46
+
47
+ | Command | Purpose |
48
+ | ---------------------- | -------------------------------------- |
49
+ | `npm run build` | Compile TypeScript to JavaScript |
50
+ | `npm run generator` | Test generation with example swagger |
51
+ | `npm run lint` | Run ESLint checks |
52
+ | `npm run lint:fix` | Auto-fix ESLint issues |
53
+ | `npm run format` | Format with Prettier |
54
+ | `npm run format:check` | Check formatting |
55
+ | `npm run type-check` | TypeScript type checking |
56
+ | `npm run validate` | Run all checks (lint + format + types) |
57
+
58
+ ### Development Tools
59
+
60
+ - **VS Code**: Recommended IDE with extensions:
61
+ - ESLint
62
+ - Prettier
63
+ - TypeScript
64
+ - EditorConfig
65
+
66
+ - **Terminal**: PowerShell (Windows) or Bash (Unix)
67
+
68
+ ## Development Workflow
69
+
70
+ ### 1. Make Changes
71
+
72
+ Edit files in `src/` directory:
73
+
74
+ ```
75
+ src/
76
+ index.ts # CLI entry point
77
+ generate.ts # OpenAPI wrapper
78
+ generators/
79
+ shared/ # Common code
80
+ use-fetch/ # useFetch generator
81
+ use-async-data/ # useAsyncData generator
82
+ nuxt-server/ # Server routes generator
83
+ ```
84
+
85
+ ### 2. Build
86
+
87
+ ```bash
88
+ npm run build
89
+ ```
90
+
91
+ **Output**: `dist/` directory with compiled JavaScript
92
+
93
+ ### 3. Test Locally
94
+
95
+ ```bash
96
+ # Interactive mode
97
+ node dist/index.js generate
98
+
99
+ # With arguments
100
+ node dist/index.js generate -i swagger.yaml -o ./test-output
101
+
102
+ # Piped input for composable type
103
+ echo "useFetch" | node dist/index.js generate -i swagger.yaml -o ./test-output
104
+ ```
105
+
106
+ ### 4. Validate Code Quality
107
+
108
+ ```bash
109
+ # Run all checks
110
+ npm run validate
111
+
112
+ # Or individually
113
+ npm run lint
114
+ npm run format:check
115
+ npm run type-check
116
+ ```
117
+
118
+ ### 5. Commit
119
+
120
+ ```bash
121
+ git add .
122
+ git commit -m "feat: add support for X"
123
+ git push
124
+ ```
125
+
126
+ **Commit Convention**: Use [Conventional Commits](https://www.conventionalcommits.org/)
127
+
128
+ - `feat:` - New feature
129
+ - `fix:` - Bug fix
130
+ - `docs:` - Documentation only
131
+ - `style:` - Code style changes
132
+ - `refactor:` - Code refactoring
133
+ - `test:` - Adding tests
134
+ - `chore:` - Build/tooling changes
135
+
136
+ ## Adding Features
137
+
138
+ ### Add a New Callback Type
139
+
140
+ **Example**: Add `onRetry` callback
141
+
142
+ **1. Update Interfaces** (`src/generators/shared/runtime/apiHelpers.ts`)
143
+
144
+ ```typescript
145
+ export interface ApiRequestOptions<T> {
146
+ // ... existing
147
+ onRetry?: (attempt: number, error: any) => void | false;
148
+ }
149
+
150
+ export interface GlobalCallbacksConfig {
151
+ // ... existing
152
+ onRetry?: (attempt: number, error: any) => void | false;
153
+ }
154
+ ```
155
+
156
+ **2. Update mergeCallbacks()** (`src/generators/shared/runtime/apiHelpers.ts`)
157
+
158
+ ```typescript
159
+ export function mergeCallbacks(/* ... */) {
160
+ // ... existing
161
+
162
+ const merged: any = {
163
+ // ... existing callbacks
164
+
165
+ onRetry: (attempt: number, error: any) => {
166
+ if (global.onRetry && shouldApplyGlobalCallback('onRetry', skipConfig, url, patterns)) {
167
+ const result = global.onRetry(attempt, error);
168
+ if (result === false) return; // Cancel local
169
+ }
170
+ if (local.onRetry) {
171
+ local.onRetry(attempt, error);
172
+ }
173
+ },
174
+ };
175
+
176
+ return merged;
177
+ }
178
+ ```
179
+
180
+ **3. Update Runtime Wrappers**
181
+
182
+ `src/generators/use-fetch/runtime/useApiRequest.ts`:
183
+
184
+ ```typescript
185
+ export function useApiRequest<T>(url: string, options?: ApiRequestOptions<T>) {
186
+ // ... existing code
187
+
188
+ // Add retry logic
189
+ const executeWithRetry = async (fn: () => Promise<T>, maxRetries = 3) => {
190
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
191
+ try {
192
+ return await fn();
193
+ } catch (error) {
194
+ if (mergedCallbacks.onRetry) {
195
+ mergedCallbacks.onRetry(attempt, error);
196
+ }
197
+
198
+ if (attempt === maxRetries) throw error;
199
+ }
200
+ }
201
+ };
202
+
203
+ // ... use executeWithRetry
204
+ }
205
+ ```
206
+
207
+ **4. Update Plugin Template** (`src/generators/shared/templates/api-callbacks-plugin.ts`)
208
+
209
+ Add documentation and examples:
210
+
211
+ ```typescript
212
+ /**
213
+ * onRetry: Called when a request fails and will be retried
214
+ * Parameters: (attempt: number, error: any)
215
+ * Return: void | false (return false to cancel local onRetry)
216
+ *
217
+ * @example
218
+ * onRetry: (attempt, error) => {
219
+ * console.log(`Retry attempt ${attempt}`);
220
+ * if (error.status === 429) {
221
+ * // Wait before retry
222
+ * return new Promise(resolve => setTimeout(resolve, 1000));
223
+ * }
224
+ * }
225
+ */
226
+ ```
227
+
228
+ **5. Update Documentation** (`docs/API-REFERENCE.md`)
229
+
230
+ Add to callback interfaces section and usage examples.
231
+
232
+ **6. Test**
233
+
234
+ ```bash
235
+ npm run build
236
+ node dist/index.js generate -i swagger.yaml -o ./test-output
237
+ ```
238
+
239
+ Verify callback is called correctly.
240
+
241
+ ### Add a New Generator Type
242
+
243
+ **Example**: Add TanStack Query generator
244
+
245
+ **1. Create Generator Directory**
246
+
247
+ ```bash
248
+ mkdir src/generators/tanstack-query
249
+ cd src/generators/tanstack-query
250
+ ```
251
+
252
+ **2. Create Files**
253
+
254
+ ```
255
+ tanstack-query/
256
+ types.ts # Re-export from shared/types.ts
257
+ parser.ts # Re-export from use-fetch/parser.ts
258
+ templates.ts # Generate TanStack Query code
259
+ generator.ts # Main orchestration
260
+ runtime/
261
+ useApiQuery.ts # TanStack Query wrapper
262
+ ```
263
+
264
+ **3. Implement Parser** (`parser.ts`)
265
+
266
+ ```typescript
267
+ // Re-export shared parser
268
+ export * from '../shared/types';
269
+ export { parseApiFile, getApiFiles } from '../use-fetch/parser';
270
+ ```
271
+
272
+ **4. Implement Templates** (`templates.ts`)
273
+
274
+ ```typescript
275
+ import type { MethodInfo } from './types';
276
+
277
+ export function generateFileHeader(): string {
278
+ return `// @ts-nocheck - TanStack Query composable\n`;
279
+ }
280
+
281
+ export function generateImports(method: MethodInfo, apiImportPath: string): string {
282
+ return `import { useQuery } from '@tanstack/vue-query';
283
+ import type { ${method.requestType}, ${method.responseType} } from '${apiImportPath}';
284
+ `;
285
+ }
286
+
287
+ export function generateFunctionBody(method: MethodInfo): string {
288
+ const url =
289
+ method.pathParams.length === 0
290
+ ? `'${method.path}'`
291
+ : `\`${method.path.replace(/{(\w+)}/g, '${params.$1}')}\``;
292
+
293
+ return `export const useQuery${method.name} = (params: ${method.requestType}) => {
294
+ return useQuery({
295
+ queryKey: ['${method.name}', params],
296
+ queryFn: async () => {
297
+ const response = await $fetch<${method.responseType}>(${url}, {
298
+ method: '${method.method}',
299
+ ${method.bodyField ? `body: params.${method.bodyField},` : ''}
300
+ });
301
+ return response;
302
+ },
303
+ });
304
+ };`;
305
+ }
306
+
307
+ export function generateComposableFile(method: MethodInfo, apiImportPath: string): string {
308
+ const header = generateFileHeader();
309
+ const imports = generateImports(method, apiImportPath);
310
+ const body = generateFunctionBody(method);
311
+
312
+ return `${header}${imports}\n${body}\n`;
313
+ }
314
+ ```
315
+
316
+ **5. Implement Generator** (`generator.ts`)
317
+
318
+ ```typescript
319
+ import * as fs from 'fs-extra';
320
+ import * as path from 'path';
321
+ import * as p from '@clack/prompts';
322
+ import prettier from 'prettier';
323
+ import { parseApiFile, scanApiFiles, type MethodInfo } from './parser';
324
+ import { generateComposableFile } from './templates';
325
+
326
+ export async function generateTanstackQueryComposables(
327
+ inputDir: string,
328
+ outputDir: string
329
+ ): Promise<void> {
330
+ p.log.step('📦 Starting TanStack Query composables generation...');
331
+
332
+ // 1. Scan and parse
333
+ const apiFiles = getApiFiles(inputDir);
334
+
335
+ const allMethods: MethodInfo[] = [];
336
+ for (const apiFile of apiFiles) {
337
+ const apiClass = parseApiFile(apiFile);
338
+ allMethods.push(...apiClass.methods);
339
+ }
340
+
341
+ // 2. Create directories
342
+ const composablesDir = path.join(outputDir, 'composables', 'tanstack-query');
343
+ await fs.ensureDir(composablesDir);
344
+
345
+ // 3. Generate files
346
+ for (const method of allMethods) {
347
+ const content = generateComposableFile(method, '../apis');
348
+ const formatted = await prettier.format(content, { parser: 'typescript' });
349
+ await fs.writeFile(path.join(composablesDir, method.fileName), formatted);
350
+ p.log.success(` ✓ useQuery${method.name}`);
351
+ }
352
+
353
+ p.log.success(`✅ Generated ${allMethods.length} TanStack Query composables`);
354
+ }
355
+ ```
356
+
357
+ **6. Register in CLI** (`src/index.ts`)
358
+
359
+ ```typescript
360
+ const composableChoice = await p.multiselect({
361
+ message: 'Select composables to generate:',
362
+ options: [
363
+ { value: 'useFetch', label: 'useFetch (Nuxt 3 useFetch)' },
364
+ { value: 'useAsyncData', label: 'useAsyncData (Nuxt 3 useAsyncData)' },
365
+ { value: 'tanstack-query', label: '@tanstack/vue-query (TanStack Query)' }, // NEW
366
+ { value: 'nuxt-server', label: 'nuxt-server (Nuxt Server Routes)' },
367
+ ],
368
+ });
369
+
370
+ // ... later
371
+ if (selectedComposables.includes('tanstack-query')) {
372
+ const { generateTanstackQueryComposables } =
373
+ await import('./generators/tanstack-query/generator');
374
+ await generateTanstackQueryComposables(input, output);
375
+ }
376
+ ```
377
+
378
+ **7. Test**
379
+
380
+ ```bash
381
+ npm run build
382
+ node dist/index.js generate -i swagger.yaml -o ./test-output
383
+ # Select TanStack Query option
384
+ ```
385
+
386
+ ### Add Parser Feature
387
+
388
+ **Example**: Extract response headers from OpenAPI
389
+
390
+ **1. Update MethodInfo** (`src/generators/shared/types.ts`)
391
+
392
+ ```typescript
393
+ export interface MethodInfo {
394
+ // ... existing
395
+ responseHeaders?: string[]; // NEW: Expected response headers
396
+ }
397
+ ```
398
+
399
+ **2. Update Parser** (`src/generators/use-fetch/parser.ts`)
400
+
401
+ ```typescript
402
+ export function extractMethodInfo(
403
+ method: MethodDeclaration,
404
+ sourceFile: SourceFile
405
+ ): MethodInfo | null {
406
+ // ... existing extraction logic
407
+
408
+ // Extract response headers from JSDoc or OpenAPI comments
409
+ const jsdocTags = method.getJsDocs();
410
+ const responseHeaders: string[] = [];
411
+
412
+ for (const jsdoc of jsdocTags) {
413
+ const tags = jsdoc.getTags();
414
+ for (const tag of tags) {
415
+ if (tag.getTagName() === 'responseHeader') {
416
+ const headerName = tag.getCommentText();
417
+ if (headerName) responseHeaders.push(headerName);
418
+ }
419
+ }
420
+ }
421
+
422
+ return {
423
+ // ... existing fields
424
+ responseHeaders: responseHeaders.length > 0 ? responseHeaders : undefined,
425
+ };
426
+ }
427
+ ```
428
+
429
+ **3. Use in Templates** (`src/generators/use-fetch/templates.ts`)
430
+
431
+ ```typescript
432
+ export function generateFunctionBody(method: MethodInfo): string {
433
+ const headerChecks = method.responseHeaders
434
+ ? method.responseHeaders.map((h) => ` console.log('${h}:', headers.get('${h}'));`).join('\n')
435
+ : '';
436
+
437
+ return `export const ${method.composableName} = (...) => {
438
+ const result = useApiRequest(...);
439
+
440
+ ${
441
+ headerChecks
442
+ ? `watch(result.data, (data) => {
443
+ if (data) {
444
+ ${headerChecks}
445
+ }
446
+ });`
447
+ : ''
448
+ }
449
+
450
+ return result;
451
+ };`;
452
+ }
453
+ ```
454
+
455
+ **4. Test**
456
+
457
+ ```bash
458
+ npm run build
459
+ node dist/index.js generate -i swagger.yaml -o ./test-output
460
+ # Check generated composables for header logging
461
+ ```
462
+
463
+ ## Testing Changes
464
+
465
+ ### Manual Testing
466
+
467
+ **1. Build**
468
+
469
+ ```bash
470
+ npm run build
471
+ ```
472
+
473
+ **2. Generate with Test Swagger**
474
+
475
+ ```bash
476
+ # Use included swagger.yaml
477
+ node dist/index.js generate -i swagger.yaml -o ./test-output
478
+ ```
479
+
480
+ **3. Check Output**
481
+
482
+ ```bash
483
+ # List generated files
484
+ ls test-output/composables/use-fetch/
485
+
486
+ # Read a generated file
487
+ cat test-output/composables/use-fetch/use-fetch-add-pet.ts
488
+ ```
489
+
490
+ **4. Test in Nuxt Project**
491
+
492
+ ```bash
493
+ # Copy to a Nuxt project
494
+ cp -r test-output/composables ~/my-nuxt-app/composables/
495
+
496
+ # In Nuxt project
497
+ cd ~/my-nuxt-app
498
+ npm run dev
499
+
500
+ # Try using composable in a page
501
+ # pages/test.vue
502
+ <script setup>
503
+ const { data } = useFetchAddPet({ pet: { name: 'Fluffy' } });
504
+ </script>
505
+ ```
506
+
507
+ ### Testing Checklist
508
+
509
+ - [ ] `npm run build` succeeds
510
+ - [ ] `npm run validate` passes
511
+ - [ ] Generation completes without errors
512
+ - [ ] All expected files are created
513
+ - [ ] Generated code has correct imports
514
+ - [ ] Generated code compiles in TypeScript
515
+ - [ ] Composables work in Nuxt project
516
+ - [ ] Callbacks fire correctly
517
+ - [ ] Types are correct
518
+ - [ ] Edge cases handled (no params, path params, etc.)
519
+
520
+ ### Debugging Tips
521
+
522
+ See [Debugging Guide](#debugging-guide) section below.
523
+
524
+ ## Code Style
525
+
526
+ ### TypeScript Style
527
+
528
+ ```typescript
529
+ // ✅ Good: Explicit types
530
+ export interface MethodInfo {
531
+ name: string;
532
+ path: string;
533
+ }
534
+
535
+ // ❌ Bad: Implicit any
536
+ export interface MethodInfo {
537
+ name;
538
+ path;
539
+ }
540
+
541
+ // ✅ Good: Readonly where possible
542
+ export interface MethodInfo {
543
+ readonly name: string;
544
+ }
545
+
546
+ // ✅ Good: Defensive null checks
547
+ if (params.length > 0) {
548
+ const type = params[0].getType();
549
+ }
550
+
551
+ // ❌ Bad: Assuming existence
552
+ const type = params[0].getType(); // Might crash if empty
553
+ ```
554
+
555
+ ### Naming Conventions
556
+
557
+ ```typescript
558
+ // Interfaces: PascalCase
559
+ interface ApiRequestOptions {}
560
+
561
+ // Functions: camelCase
562
+ function generateComposableFile() {}
563
+
564
+ // Constants: UPPER_SNAKE_CASE
565
+ const BASE_PATH = '/api';
566
+
567
+ // Files: kebab-case
568
+ // use-api-request.ts (not useApiRequest.ts)
569
+ ```
570
+
571
+ ### Error Handling
572
+
573
+ ```typescript
574
+ // ✅ Good: Try-catch with specific error
575
+ try {
576
+ const method = extractMethodInfo(node);
577
+ if (method) methods.push(method);
578
+ } catch (error) {
579
+ console.warn(`Warning: Could not parse ${node.getName()}: ${error}`);
580
+ // Continue processing
581
+ }
582
+
583
+ // ❌ Bad: Silent failure
584
+ try {
585
+ const method = extractMethodInfo(node);
586
+ methods.push(method);
587
+ } catch {}
588
+
589
+ // ✅ Good: Helpful error messages
590
+ throw new Error(`APIs directory not found: ${apisDir}
591
+
592
+ Make sure you've generated OpenAPI files first:
593
+ nxh generate -i swagger.yaml -o ./output
594
+ `);
595
+
596
+ // ❌ Bad: Vague error
597
+ throw new Error('Directory not found');
598
+ ```
599
+
600
+ ### Comments
601
+
602
+ ```typescript
603
+ // ✅ Good: JSDoc for public APIs
604
+ /**
605
+ * Generate Nuxt composables from OpenAPI specification
606
+ * @param inputDir Directory containing generated TypeScript files
607
+ * @param outputDir Output directory for composables
608
+ * @returns Promise that resolves when generation completes
609
+ */
610
+ export async function generate(inputDir: string, outputDir: string): Promise<void> {}
611
+
612
+ // ✅ Good: Inline for complex logic
613
+ // Extract path from return statement: return { path: '/pet' }
614
+ const pathProperty = returnObj.getProperty('path');
615
+
616
+ // ❌ Bad: Stating the obvious
617
+ // Increment counter
618
+ counter++;
619
+ ```
620
+
621
+ ## Common Tasks
622
+
623
+ ### Task: Add Support for a New HTTP Method
624
+
625
+ **Example**: Add PATCH method support
626
+
627
+ **1. Verify Parser Handles It**
628
+
629
+ Check `src/generators/use-fetch/parser.ts`:
630
+
631
+ ```typescript
632
+ // HTTP method is extracted from returnObj.method property
633
+ // Should automatically work if OpenAPI Generator includes it
634
+ ```
635
+
636
+ **2. Update Type** (if restricted)
637
+
638
+ ```typescript
639
+ // In types.ts, the http method field is httpMethod: string
640
+ // PATCH is already supported — no type change needed
641
+ ```
642
+
643
+ **3. Test**
644
+
645
+ ```bash
646
+ # Create OpenAPI with PATCH endpoint
647
+ # Generate and verify
648
+ npm run build
649
+ node dist/index.js generate -i swagger-with-patch.yaml -o ./test
650
+ ```
651
+
652
+ ### Task: Change Generated File Names
653
+
654
+ **Example**: Use PascalCase instead of kebab-case
655
+
656
+ **1. Update Converter** (`src/generators/use-fetch/parser.ts`)
657
+
658
+ ```typescript
659
+ import { pascalCase } from 'change-case';
660
+
661
+ export function extractMethodInfo(/* ... */): MethodInfo {
662
+ // ... existing
663
+
664
+ return {
665
+ // ... existing
666
+ fileName: `${pascalCase(methodName)}.ts`, // NEW: PascalCase
667
+ };
668
+ }
669
+ ```
670
+
671
+ **2. Test**
672
+
673
+ ```bash
674
+ npm run build
675
+ node dist/index.js generate -i swagger.yaml -o ./test
676
+ ls test/composables/use-fetch/
677
+ # Should see: UseFetchAddPet.ts instead of use-fetch-add-pet.ts
678
+ ```
679
+
680
+ ### Task: Add CLI Flag
681
+
682
+ **Example**: Add `--skip-formatting` flag
683
+
684
+ **1. Update CLI** (`src/index.ts`)
685
+
686
+ ```typescript
687
+ program
688
+ .command('generate')
689
+ .option('-i, --input <file>', 'OpenAPI file')
690
+ .option('-o, --output <dir>', 'Output directory')
691
+ .option('--skip-formatting', 'Skip Prettier formatting') // NEW
692
+ .action(async (options) => {
693
+ // Pass to generators
694
+ await generateUseFetchComposables(input, output, {
695
+ skipFormatting: options.skipFormatting,
696
+ });
697
+ });
698
+ ```
699
+
700
+ **2. Update Generator** (`src/generators/use-fetch/generator.ts`)
701
+
702
+ ```typescript
703
+ export async function generateUseFetchComposables(
704
+ inputDir: string,
705
+ outputDir: string,
706
+ options?: { skipFormatting?: boolean }
707
+ ): Promise<void> {
708
+ // ... generate content
709
+
710
+ const finalContent = options?.skipFormatting
711
+ ? content
712
+ : await prettier.format(content, { parser: 'typescript' });
713
+
714
+ await fs.writeFile(filePath, finalContent);
715
+ }
716
+ ```
717
+
718
+ **3. Test**
719
+
720
+ ```bash
721
+ npm run build
722
+ node dist/index.js generate -i swagger.yaml -o ./test --skip-formatting
723
+ ```
724
+
725
+ ## Extension Points
726
+
727
+ ### 1. Custom Runtime Wrappers
728
+
729
+ **Location**: `src/generators/*/runtime/`
730
+
731
+ **Purpose**: Modify behavior of generated composables
732
+
733
+ **Example**: Add automatic retry
734
+
735
+ ```typescript
736
+ // src/generators/use-fetch/runtime/useApiRequest.ts
737
+ export function useApiRequest<T>(url: string, options?: ApiRequestOptions<T>) {
738
+ const maxRetries = options?.maxRetries ?? 3;
739
+ let attempt = 0;
740
+
741
+ const fetchWithRetry = async () => {
742
+ try {
743
+ return await useFetch<T>(url, options);
744
+ } catch (error) {
745
+ if (attempt < maxRetries) {
746
+ attempt++;
747
+ return fetchWithRetry();
748
+ }
749
+ throw error;
750
+ }
751
+ };
752
+
753
+ return fetchWithRetry();
754
+ }
755
+ ```
756
+
757
+ ### 2. Custom Template Functions
758
+
759
+ **Location**: `src/generators/*/templates.ts`
760
+
761
+ **Purpose**: Change generated code format
762
+
763
+ **Example**: Add banner comment
764
+
765
+ ```typescript
766
+ export function generateFileHeader(): string {
767
+ return `/**
768
+ * AUTO-GENERATED - DO NOT EDIT
769
+ * Generated by Nuxt Generator
770
+ * Timestamp: ${new Date().toISOString()}
771
+ */
772
+ // @ts-nocheck
773
+ `;
774
+ }
775
+ ```
776
+
777
+ ### 3. Custom Parser Logic
778
+
779
+ **Location**: `src/generators/*/parser.ts`
780
+
781
+ **Purpose**: Extract additional metadata
782
+
783
+ **Example**: Extract JSDoc descriptions
784
+
785
+ ```typescript
786
+ export function extractMethodInfo(method: MethodDeclaration): MethodInfo {
787
+ // ...existing
788
+
789
+ const jsDocs = method.getJsDocs();
790
+ const description = jsDocs[0]?.getDescription() ?? '';
791
+
792
+ return {
793
+ // ...existing
794
+ description,
795
+ };
796
+ }
797
+ ```
798
+
799
+ ## Debugging Guide
800
+
801
+ ### Common Issues
802
+
803
+ #### Build Fails
804
+
805
+ **Symptom**: `npm run build` errors
806
+
807
+ **Solution**:
808
+
809
+ ```bash
810
+ # Clean and rebuild
811
+ rm -rf dist/
812
+ npm run build
813
+
814
+ # Check TypeScript errors
815
+ npm run type-check
816
+ ```
817
+
818
+ #### Parser Not Finding Methods
819
+
820
+ **Symptom**: "Found 0 methods"
821
+
822
+ **Debug**:
823
+
824
+ ```typescript
825
+ // Add logging in parser.ts
826
+ export function parseApiFile(filePath: string): ApiClassInfo {
827
+ console.log('Parsing file:', filePath);
828
+
829
+ const classes = sourceFile.getClasses();
830
+ console.log(
831
+ 'Found classes:',
832
+ classes.map((c) => c.getName())
833
+ );
834
+
835
+ for (const cls of classes) {
836
+ const methods = cls.getMethods();
837
+ console.log(
838
+ `Methods in ${cls.getName()}:`,
839
+ methods.map((m) => m.getName())
840
+ );
841
+ }
842
+
843
+ // Continue...
844
+ }
845
+ ```
846
+
847
+ #### Generated Code Has Type Errors
848
+
849
+ **Symptom**: TypeScript errors in generated files
850
+
851
+ **Debug**:
852
+
853
+ 1. Check import paths (relative vs absolute)
854
+ 2. Verify type exports from OpenAPI Generator
855
+ 3. Check `responseType` and `requestType` extraction
856
+
857
+ ```typescript
858
+ // Add logging in parser
859
+ console.log('Response type:', responseType);
860
+ console.log('Request type:', requestType);
861
+ ```
862
+
863
+ #### Callbacks Not Firing
864
+
865
+ **Symptom**: `onSuccess` never called
866
+
867
+ **Debug**:
868
+
869
+ ```typescript
870
+ // In useApiRequest.ts
871
+ watch(
872
+ () => [result.data.value, result.error.value, result.pending.value] as const,
873
+ ([data, error, pending], [prevData, prevError, prevPending]) => {
874
+ console.log('Watch triggered:', { data, error, pending });
875
+
876
+ if (data && data !== prevData) {
877
+ console.log('Calling onSuccess');
878
+ mergedCallbacks.onSuccess?.(data);
879
+ }
880
+ },
881
+ { immediate: true }
882
+ );
883
+ ```
884
+
885
+ ### DevTools
886
+
887
+ **ts-morph Explorer**:
888
+
889
+ ```typescript
890
+ // Explore AST structure
891
+ import { Project } from 'ts-morph';
892
+
893
+ const project = new Project();
894
+ const sourceFile = project.addSourceFileAtPath('PetApi.ts');
895
+
896
+ // Print entire AST
897
+ console.log(sourceFile.getFullText());
898
+
899
+ // Print structure
900
+ sourceFile.getDescendants().forEach((node) => {
901
+ console.log(node.getKindName(), node.getText().substring(0, 50));
902
+ });
903
+ ```
904
+
905
+ **Prettier Test**:
906
+
907
+ ```typescript
908
+ // Test formatting
909
+ import prettier from 'prettier';
910
+
911
+ const code = `export const test=()=>{return"hello"}`;
912
+ const formatted = await prettier.format(code, { parser: 'typescript' });
913
+ console.log(formatted);
914
+ ```
915
+
916
+ ---
917
+
918
+ **Next**: [Troubleshooting Common Issues](./TROUBLESHOOTING.md)