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,338 @@
1
+ // @ts-nocheck
2
+ /**
3
+ * Global API Callbacks Plugin
4
+ *
5
+ * ⚠️ IMPORTANT: This file is NEVER regenerated - your changes are safe!
6
+ *
7
+ * This plugin allows you to configure global callbacks for all API requests
8
+ * made with useFetch* and useAsyncData* composables.
9
+ *
10
+ * 📚 Three ways to control global callbacks:
11
+ *
12
+ * 1️⃣ OPTION 1: skipGlobalCallbacks (disable from the call)
13
+ * Skip global callbacks for specific requests
14
+ * Example:
15
+ * useFetchGetPets({ skipGlobalCallbacks: true })
16
+ * useFetchGetPets({ skipGlobalCallbacks: ['onSuccess'] })
17
+ *
18
+ * 2️⃣ OPTION 2: return false (disable from the plugin)
19
+ * Global callbacks can return false to prevent local callback execution
20
+ * Example:
21
+ * onError: (error) => {
22
+ * if (error.statusCode === 401) {
23
+ * navigateTo('/login');
24
+ * return false; // Don't execute local onError
25
+ * }
26
+ * }
27
+ *
28
+ * 3️⃣ OPTION 3: patterns (URL matching)
29
+ * Only apply callbacks to URLs matching specific patterns
30
+ * Example:
31
+ * patterns: ['/api/**', '/api/v2/*']
32
+ */
33
+ export default defineNuxtPlugin(() => {
34
+ // Uncomment and customize the callbacks you need
35
+ const globalCallbacks = {
36
+ // ========================================================================
37
+ // OPTION 3: URL Pattern Matching (OPTIONAL)
38
+ // ========================================================================
39
+ // Only apply global callbacks to URLs matching these patterns
40
+ // Use ** to match any path (including nested), * to match single segment
41
+ // If omitted or empty, callbacks apply to ALL requests
42
+ // patterns: ['/api/**'], // Only internal APIs
43
+ // patterns: ['/api/v1/**', '/api/v2/**'], // Multiple API versions
44
+ // patterns: ['**/public/**'], // All public endpoints
45
+ // ========================================================================
46
+ // onRequest: Called before every request
47
+ // ========================================================================
48
+ // Use cases:
49
+ // - Add authentication headers globally
50
+ // - Log all API calls
51
+ // - Add request timestamps
52
+ // - Modify request body/headers/params
53
+ // onRequest: (context) => {
54
+ // console.log(`[API] ${context.method} ${context.url}`);
55
+ //
56
+ // // Example 1: Add auth token to all requests
57
+ // // const token = useCookie('auth-token').value;
58
+ // // if (token) {
59
+ // // return {
60
+ // // headers: { 'Authorization': `Bearer ${token}` }
61
+ // // };
62
+ // // }
63
+ //
64
+ // // Example 2: Add request timestamp
65
+ // // return {
66
+ // // headers: { 'X-Request-Time': new Date().toISOString() }
67
+ // // };
68
+ //
69
+ // // Example 3: Block local onRequest (OPTION 2)
70
+ // // return false;
71
+ // },
72
+ // ========================================================================
73
+ // onSuccess: Called when request succeeds
74
+ // ========================================================================
75
+ // Use cases:
76
+ // - Show success notifications/toasts
77
+ // - Track successful operations
78
+ // - Update analytics
79
+ // - Cache responses
80
+ // onSuccess: (data, context) => {
81
+ // // Example 1: Success notification
82
+ // // const { $toast } = useNuxtApp();
83
+ // // $toast?.success('✅ Operation successful');
84
+ //
85
+ // // Example 2: Track analytics
86
+ // // trackEvent('api_success', { url: context?.url });
87
+ //
88
+ // // Example 3: Log response data (development only)
89
+ // // if (process.dev) {
90
+ // // console.log('API Response:', data);
91
+ // // }
92
+ //
93
+ // // Example 4: Cache specific responses
94
+ // // if (context?.url.includes('/api/config')) {
95
+ // // localStorage.setItem('app-config', JSON.stringify(data));
96
+ // // }
97
+ //
98
+ // // Example 5: Block local onSuccess for certain cases (OPTION 2)
99
+ // // if (data.status === 'pending_approval') {
100
+ // // $toast?.info('⏳ Awaiting approval');
101
+ // // return false; // Don't execute local onSuccess
102
+ // // }
103
+ // },
104
+ // ========================================================================
105
+ // onError: Called when request fails
106
+ // ========================================================================
107
+ // Use cases:
108
+ // - Handle authentication errors globally (401, 403)
109
+ // - Show error notifications
110
+ // - Log errors to monitoring service
111
+ // - Handle network errors
112
+ // - Retry logic
113
+ // onError: (error, context) => {
114
+ // console.error('[API Error]', error);
115
+ // const { $toast } = useNuxtApp();
116
+ //
117
+ // // Example 1: Handle authentication errors (OPTION 2)
118
+ // if (error.statusCode === 401) {
119
+ // $toast?.warning('⚠️ Session expired - redirecting to login...');
120
+ // navigateTo('/login');
121
+ // return false; // Don't execute local onError (avoiding duplicate messages)
122
+ // }
123
+ //
124
+ // // Example 2: Handle forbidden errors
125
+ // // if (error.statusCode === 403) {
126
+ // // $toast?.error('❌ Access denied');
127
+ // // navigateTo('/');
128
+ // // return false;
129
+ // // }
130
+ //
131
+ // // Example 3: Handle server errors
132
+ // // if (error.statusCode >= 500) {
133
+ // // $toast?.error('❌ Server error - please try again later');
134
+ // // // Log to monitoring service
135
+ // // // logErrorToSentry(error);
136
+ // // }
137
+ //
138
+ // // Example 4: Handle rate limiting
139
+ // // if (error.statusCode === 429) {
140
+ // // const retryAfter = error.data?.retryAfter || 60;
141
+ // // $toast?.warning(`⏳ Too many requests - retry in ${retryAfter}s`);
142
+ // // return false; // Don't show duplicate error in component
143
+ // // }
144
+ //
145
+ // // Example 5: Handle network errors
146
+ // // if (error.message === 'Network request failed') {
147
+ // // $toast?.error('❌ Network error - check your connection');
148
+ // // }
149
+ //
150
+ // // Example 6: Generic error notification
151
+ // // $toast?.error(`❌ ${error.message || 'An error occurred'}`);
152
+ //
153
+ // // Allow local onError to execute (return true or don't return)
154
+ // },
155
+ // ========================================================================
156
+ // onFinish: Called when request completes (success or error)
157
+ // ========================================================================
158
+ // Use cases:
159
+ // - Hide loading indicators
160
+ // - Track request completion
161
+ // - Clean up resources
162
+ // - Update request counters
163
+ // onFinish: (context) => {
164
+ // // Example 1: Track API call completion
165
+ // // const duration = Date.now() - context.startTime;
166
+ // // trackMetric('api_call_duration', duration, { url: context.url });
167
+ //
168
+ // // Example 2: Log request completion
169
+ // // console.log(
170
+ // // `[API] ${context.url} - ${context.success ? '✅ Success' : '❌ Failed'}`
171
+ // // );
172
+ //
173
+ // // Example 3: Update request counter
174
+ // // const store = useRequestStore();
175
+ // // store.decrementPendingRequests();
176
+ //
177
+ // // Example 4: Clean up global loading state
178
+ // // const { $loading } = useNuxtApp();
179
+ // // $loading?.hide();
180
+ // },
181
+ };
182
+ return {
183
+ provide: {
184
+ getGlobalApiCallbacks: () => globalCallbacks,
185
+ },
186
+ };
187
+ });
188
+ // ============================================================================
189
+ // USAGE EXAMPLES
190
+ // ============================================================================
191
+ /**
192
+ * Example 1: Use global callbacks (default behavior)
193
+ *
194
+ * const { data, error } = useFetchGetPets();
195
+ * // ✅ All global callbacks execute automatically
196
+ */
197
+ /**
198
+ * Example 2: Skip ALL global callbacks (OPTION 1)
199
+ *
200
+ * const { data, error } = useFetchGetPets({
201
+ * skipGlobalCallbacks: true,
202
+ * });
203
+ * // ❌ No global callbacks execute
204
+ */
205
+ /**
206
+ * Example 3: Skip SPECIFIC global callbacks (OPTION 1)
207
+ *
208
+ * const { data, error } = useFetchUpdatePet(id, pet, {
209
+ * skipGlobalCallbacks: ['onSuccess'], // Skip global onSuccess only
210
+ * onSuccess: (data) => {
211
+ * // Only this local callback executes
212
+ * console.log('Pet updated:', data);
213
+ * }
214
+ * });
215
+ * // ✅ Global onError still executes
216
+ * // ❌ Global onSuccess skipped
217
+ * // ✅ Local onSuccess executes
218
+ */
219
+ /**
220
+ * Example 4: Global callback prevents local execution (OPTION 2)
221
+ *
222
+ * // In plugin:
223
+ * onError: (error) => {
224
+ * if (error.statusCode === 401) {
225
+ * navigateTo('/login');
226
+ * return false; // Don't execute local onError
227
+ * }
228
+ * }
229
+ *
230
+ * // In component:
231
+ * const { data, error } = useFetchGetPets({
232
+ * onError: (error) => {
233
+ * // ❌ This won't execute for 401 errors (global returned false)
234
+ * // ✅ This executes for other errors (404, 500, etc.)
235
+ * console.error('Failed to load pets:', error);
236
+ * }
237
+ * });
238
+ */
239
+ /**
240
+ * Example 5: URL pattern matching (OPTION 3)
241
+ *
242
+ * // In plugin:
243
+ * patterns: ['/api/public/**']
244
+ *
245
+ * // In components:
246
+ * useFetchGetPets(); // URL: /api/public/pets ✅ Global callbacks execute
247
+ * useFetchGetUser(); // URL: /api/users/me ❌ Global callbacks skipped
248
+ * useFetchGetPublicConfig(); // URL: /api/public/config ✅ Global callbacks execute
249
+ */
250
+ /**
251
+ * Example 6: Combine all options
252
+ *
253
+ * // In plugin:
254
+ * patterns: ['/api/**'],
255
+ * onError: (error) => {
256
+ * if (error.statusCode === 401) return false;
257
+ * }
258
+ *
259
+ * // In component:
260
+ * const { data } = useFetchCreatePet(pet, {
261
+ * skipGlobalCallbacks: ['onSuccess'], // Skip global success toast
262
+ * onSuccess: (pet) => {
263
+ * // Show custom success message
264
+ * toast.success(`🐕 ${pet.name} added successfully!`);
265
+ * },
266
+ * onError: (error) => {
267
+ * // This won't execute for 401 (global returns false)
268
+ * // This executes for other errors
269
+ * console.error('Failed to create pet:', error);
270
+ * }
271
+ * });
272
+ */
273
+ // ============================================================================
274
+ // COMMON PATTERNS
275
+ // ============================================================================
276
+ /**
277
+ * Pattern 1: Toast notifications for all operations
278
+ *
279
+ * const globalCallbacks = {
280
+ * onSuccess: () => {
281
+ * useNuxtApp().$toast?.success('✅ Success');
282
+ * },
283
+ * onError: (error) => {
284
+ * if (error.statusCode === 401) {
285
+ * navigateTo('/login');
286
+ * return false;
287
+ * }
288
+ * useNuxtApp().$toast?.error(`❌ ${error.message}`);
289
+ * }
290
+ * };
291
+ */
292
+ /**
293
+ * Pattern 2: Authentication + logging
294
+ *
295
+ * const globalCallbacks = {
296
+ * onRequest: (context) => {
297
+ * console.log(`[API] ${context.method} ${context.url}`);
298
+ * const token = useCookie('auth-token').value;
299
+ * if (token) {
300
+ * return { headers: { 'Authorization': `Bearer ${token}` } };
301
+ * }
302
+ * },
303
+ * onError: (error) => {
304
+ * if (error.statusCode === 401) {
305
+ * useCookie('auth-token').value = null;
306
+ * navigateTo('/login');
307
+ * return false;
308
+ * }
309
+ * }
310
+ * };
311
+ */
312
+ /**
313
+ * Pattern 3: Analytics tracking
314
+ *
315
+ * const globalCallbacks = {
316
+ * onSuccess: (data, context) => {
317
+ * trackEvent('api_success', { endpoint: context?.url });
318
+ * },
319
+ * onError: (error, context) => {
320
+ * trackEvent('api_error', {
321
+ * endpoint: context?.url,
322
+ * statusCode: error.statusCode
323
+ * });
324
+ * }
325
+ * };
326
+ */
327
+ /**
328
+ * Pattern 4: Loading states
329
+ *
330
+ * const globalCallbacks = {
331
+ * onRequest: () => {
332
+ * useLoadingStore().increment();
333
+ * },
334
+ * onFinish: () => {
335
+ * useLoadingStore().decrement();
336
+ * }
337
+ * };
338
+ */
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Shared types used by multiple generators
3
+ */
4
+ export interface MethodInfo {
5
+ name: string;
6
+ composableName: string;
7
+ requestType?: string;
8
+ responseType: string;
9
+ httpMethod: string;
10
+ path: string;
11
+ hasBody: boolean;
12
+ bodyField?: string;
13
+ hasQueryParams: boolean;
14
+ queryParams: string[];
15
+ pathParams: string[];
16
+ headers: Record<string, string>;
17
+ description?: string;
18
+ hasRawMethod: boolean;
19
+ rawMethodName?: string;
20
+ paramsShape?: 'flat' | 'nested';
21
+ }
22
+ export interface ApiClassInfo {
23
+ className: string;
24
+ methods: MethodInfo[];
25
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Shared types used by multiple generators
3
+ */
4
+ export {};
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Placeholder for TanStack Query composables generator
3
+ * TODO: Implement TanStack Query generation
4
+ */
5
+ export declare function generateTanstackQueryComposables(inputDir: string, outputDir: string): void;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Placeholder for TanStack Query composables generator
3
+ * TODO: Implement TanStack Query generation
4
+ */
5
+ export function generateTanstackQueryComposables(inputDir, outputDir) {
6
+ console.log('\n📦 @tanstack/vue-query generator\n');
7
+ console.log(` Input: ${inputDir}`);
8
+ console.log(` Output: ${outputDir}`);
9
+ console.log('\n⚠️ This generator is not yet implemented.');
10
+ console.log(' Coming soon!\n');
11
+ }
@@ -0,0 +1,5 @@
1
+ import { type GenerateOptions } from './templates.js';
2
+ /**
3
+ * Main function to generate useAsyncData composables
4
+ */
5
+ export declare function generateUseAsyncDataComposables(inputDir: string, outputDir: string, options?: GenerateOptions): Promise<void>;
@@ -0,0 +1,156 @@
1
+ import * as path from 'path';
2
+ import fs from 'fs-extra';
3
+ import { fileURLToPath } from 'url';
4
+ import { format } from 'prettier';
5
+ import { getApiFiles as getApiFilesOfficial, parseApiFile as parseApiFileOfficial, } from './parser.js';
6
+ import { getApiFiles as getApiFilesHeyApi, parseApiFile as parseApiFileHeyApi, } from '../shared/parsers/heyapi-parser.js';
7
+ import { generateComposableFile, generateRawComposableFile, generateIndexFile, } from './templates.js';
8
+ import { p, logSuccess, logError } from '../../cli/logger.js';
9
+ /**
10
+ * Main function to generate useAsyncData composables
11
+ */
12
+ export async function generateUseAsyncDataComposables(inputDir, outputDir, options) {
13
+ const mainSpinner = p.spinner();
14
+ // Select parser based on chosen backend
15
+ const getApiFiles = options?.backend === 'heyapi' ? getApiFilesHeyApi : getApiFilesOfficial;
16
+ const parseApiFile = options?.backend === 'heyapi' ? parseApiFileHeyApi : parseApiFileOfficial;
17
+ // 1. Get all API files
18
+ mainSpinner.start('Scanning API files');
19
+ const apiFiles = getApiFiles(inputDir);
20
+ mainSpinner.stop(`Found ${apiFiles.length} API file(s)`);
21
+ if (apiFiles.length === 0) {
22
+ throw new Error('No API files found in the input directory');
23
+ }
24
+ // 2. Parse each API file
25
+ mainSpinner.start('Parsing API files');
26
+ const allMethods = [];
27
+ for (const file of apiFiles) {
28
+ const fileName = path.basename(file);
29
+ try {
30
+ const apiInfo = parseApiFile(file);
31
+ allMethods.push(...apiInfo.methods);
32
+ }
33
+ catch (error) {
34
+ logError(`Error parsing ${fileName}: ${error}`);
35
+ }
36
+ }
37
+ mainSpinner.stop(`Found ${allMethods.length} methods to generate`);
38
+ if (allMethods.length === 0) {
39
+ p.log.warn('No methods found to generate');
40
+ return;
41
+ }
42
+ // 3. Clean and create output directories
43
+ mainSpinner.start('Preparing output directories');
44
+ const composablesDir = path.join(outputDir, 'composables');
45
+ const runtimeDir = path.join(outputDir, 'runtime');
46
+ const sharedRuntimeDir = path.join(outputDir, 'shared', 'runtime');
47
+ await fs.emptyDir(composablesDir);
48
+ await fs.ensureDir(runtimeDir);
49
+ await fs.ensureDir(sharedRuntimeDir);
50
+ mainSpinner.stop('Output directories ready');
51
+ // 4. Copy runtime helpers
52
+ mainSpinner.start('Copying runtime files');
53
+ // Derive __dirname equivalent for ESM
54
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
55
+ // When compiled, __dirname points to dist/generators/use-async-data/
56
+ // We need to go back to src/ to get the original .ts files
57
+ // Copy useApiAsyncData.ts
58
+ const runtimeSource = path.resolve(__dirname, '../../../src/generators/use-async-data/runtime/useApiAsyncData.ts');
59
+ const runtimeDest = path.join(runtimeDir, 'useApiAsyncData.ts');
60
+ await fs.copyFile(runtimeSource, runtimeDest);
61
+ // Copy useApiAsyncDataRaw.ts
62
+ const runtimeRawSource = path.resolve(__dirname, '../../../src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts');
63
+ const runtimeRawDest = path.join(runtimeDir, 'useApiAsyncDataRaw.ts');
64
+ await fs.copyFile(runtimeRawSource, runtimeRawDest);
65
+ // Copy shared apiHelpers.ts
66
+ const sharedHelpersSource = path.resolve(__dirname, '../../../src/generators/shared/runtime/apiHelpers.ts');
67
+ const sharedHelpersDest = path.join(sharedRuntimeDir, 'apiHelpers.ts');
68
+ await fs.copyFile(sharedHelpersSource, sharedHelpersDest);
69
+ mainSpinner.stop('Runtime files copied');
70
+ // 5. Calculate relative import path from composables to APIs
71
+ const relativePath = calculateRelativeImportPath(composablesDir, inputDir);
72
+ // 6. Generate each composable (normal + Raw if available)
73
+ mainSpinner.start('Generating composables');
74
+ let successCount = 0;
75
+ let errorCount = 0;
76
+ const generatedComposableNames = [];
77
+ for (const method of allMethods) {
78
+ // Generate normal version
79
+ try {
80
+ const code = generateComposableFile(method, relativePath, options);
81
+ const formattedCode = await formatCode(code);
82
+ const composableName = method.composableName.replace(/^useFetch/, 'useAsyncData');
83
+ const fileName = `${composableName}.ts`;
84
+ const filePath = path.join(composablesDir, fileName);
85
+ await fs.writeFile(filePath, formattedCode, 'utf-8');
86
+ generatedComposableNames.push(composableName);
87
+ successCount++;
88
+ }
89
+ catch (error) {
90
+ logError(`Error generating ${method.composableName}: ${error}`);
91
+ errorCount++;
92
+ }
93
+ // Generate Raw version if available
94
+ if (method.hasRawMethod && method.rawMethodName) {
95
+ try {
96
+ const code = generateRawComposableFile(method, relativePath, options);
97
+ const formattedCode = await formatCode(code);
98
+ const composableName = `useAsyncData${method.rawMethodName.replace(/Raw$/, '')}Raw`;
99
+ const fileName = `${composableName}.ts`;
100
+ const filePath = path.join(composablesDir, fileName);
101
+ await fs.writeFile(filePath, formattedCode, 'utf-8');
102
+ generatedComposableNames.push(composableName);
103
+ successCount++;
104
+ }
105
+ catch (error) {
106
+ logError(`Error generating ${method.composableName} (Raw): ${error}`);
107
+ errorCount++;
108
+ }
109
+ }
110
+ }
111
+ // 7. Generate index.ts
112
+ const indexCode = generateIndexFile(generatedComposableNames);
113
+ const formattedIndex = await formatCode(indexCode);
114
+ await fs.writeFile(path.join(outputDir, 'index.ts'), formattedIndex, 'utf-8');
115
+ mainSpinner.stop(`Generated ${successCount} composables`);
116
+ // 8. Summary
117
+ if (errorCount > 0) {
118
+ p.log.warn(`Completed with ${errorCount} error(s)`);
119
+ }
120
+ logSuccess(`Generated ${successCount} useAsyncData composable(s) in ${outputDir}`);
121
+ }
122
+ /**
123
+ * Calculate relative import path from composables to APIs
124
+ */
125
+ function calculateRelativeImportPath(composablesDir, inputDir) {
126
+ // Import from the root index.ts which exports apis, models, and runtime
127
+ let relativePath = path.relative(composablesDir, inputDir);
128
+ // Convert Windows paths to Unix-style
129
+ relativePath = relativePath.replace(/\\/g, '/');
130
+ // Ensure it starts with './' or '../'
131
+ if (!relativePath.startsWith('.')) {
132
+ relativePath = './' + relativePath;
133
+ }
134
+ // Remove .ts extension and trailing /
135
+ relativePath = relativePath.replace(/\.ts$/, '').replace(/\/$/, '');
136
+ return relativePath;
137
+ }
138
+ /**
139
+ * Format code with Prettier
140
+ */
141
+ async function formatCode(code) {
142
+ try {
143
+ return await format(code, {
144
+ parser: 'typescript',
145
+ semi: true,
146
+ singleQuote: true,
147
+ trailingComma: 'es5',
148
+ printWidth: 80,
149
+ tabWidth: 2,
150
+ });
151
+ }
152
+ catch {
153
+ p.log.warn('Could not format code with Prettier');
154
+ return code;
155
+ }
156
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Re-export all parser functionality from use-fetch
3
+ * The parsing logic is the same for both generators
4
+ */
5
+ export * from '../use-fetch/parser.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Re-export all parser functionality from use-fetch
3
+ * The parsing logic is the same for both generators
4
+ */
5
+ export * from '../use-fetch/parser.js';
@@ -0,0 +1,38 @@
1
+ import { type ApiRequestOptions as BaseApiRequestOptions } from '../../shared/runtime/apiHelpers.js';
2
+ /**
3
+ * Extended options specific to useAsyncData
4
+ */
5
+ export interface ApiAsyncDataOptions<T> extends BaseApiRequestOptions<T> {
6
+ /**
7
+ * Whether to fetch data immediately on mount (default: true)
8
+ */
9
+ immediate?: boolean;
10
+ /**
11
+ * Lazy mode: don't block navigation (default: false)
12
+ */
13
+ lazy?: boolean;
14
+ /**
15
+ * Server-side rendering mode (default: true)
16
+ */
17
+ server?: boolean;
18
+ /**
19
+ * Deduplicate requests with the same key (default: 'cancel')
20
+ */
21
+ dedupe?: 'cancel' | 'defer';
22
+ /**
23
+ * Disable automatic refresh when reactive params change.
24
+ * Set to false to prevent re-fetching when params/url refs update.
25
+ * @default true
26
+ */
27
+ watch?: boolean;
28
+ }
29
+ /**
30
+ * Generic wrapper for API calls using Nuxt's useAsyncData
31
+ * Supports:
32
+ * - Lifecycle callbacks (onRequest, onSuccess, onError, onFinish)
33
+ * - Request modification via onRequest return value
34
+ * - Transform and pick operations
35
+ * - Global headers from useApiHeaders or $getApiHeaders
36
+ * - Watch pattern for reactive parameters
37
+ */
38
+ export declare function useApiAsyncData<T>(key: string, url: string | (() => string), options?: ApiAsyncDataOptions<T>): any;