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.
- package/.editorconfig +26 -0
- package/.prettierignore +17 -0
- package/.prettierrc.json +12 -0
- package/CONTRIBUTING.md +292 -0
- package/INSTRUCTIONS.md +327 -0
- package/LICENSE +202 -0
- package/README.md +202 -0
- package/dist/cli/config.d.ts +57 -0
- package/dist/cli/config.js +85 -0
- package/dist/cli/logger.d.ts +44 -0
- package/dist/cli/logger.js +58 -0
- package/dist/cli/logo.d.ts +6 -0
- package/dist/cli/logo.js +21 -0
- package/dist/cli/messages.d.ts +65 -0
- package/dist/cli/messages.js +86 -0
- package/dist/cli/prompts.d.ts +30 -0
- package/dist/cli/prompts.js +118 -0
- package/dist/cli/types.d.ts +43 -0
- package/dist/cli/types.js +4 -0
- package/dist/cli/utils.d.ts +26 -0
- package/dist/cli/utils.js +45 -0
- package/dist/generate.d.ts +6 -0
- package/dist/generate.js +48 -0
- package/dist/generators/nuxt-server/bff-templates.d.ts +25 -0
- package/dist/generators/nuxt-server/bff-templates.js +737 -0
- package/dist/generators/nuxt-server/generator.d.ts +7 -0
- package/dist/generators/nuxt-server/generator.js +206 -0
- package/dist/generators/nuxt-server/parser.d.ts +5 -0
- package/dist/generators/nuxt-server/parser.js +5 -0
- package/dist/generators/nuxt-server/templates.d.ts +35 -0
- package/dist/generators/nuxt-server/templates.js +412 -0
- package/dist/generators/nuxt-server/types.d.ts +5 -0
- package/dist/generators/nuxt-server/types.js +5 -0
- package/dist/generators/shared/parsers/heyapi-parser.d.ts +11 -0
- package/dist/generators/shared/parsers/heyapi-parser.js +248 -0
- package/dist/generators/shared/parsers/official-parser.d.ts +5 -0
- package/dist/generators/shared/parsers/official-parser.js +5 -0
- package/dist/generators/shared/runtime/apiHelpers.d.ts +183 -0
- package/dist/generators/shared/runtime/apiHelpers.js +268 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.d.ts +178 -0
- package/dist/generators/shared/templates/api-callbacks-plugin.js +338 -0
- package/dist/generators/shared/types.d.ts +25 -0
- package/dist/generators/shared/types.js +4 -0
- package/dist/generators/tanstack-query/generator.d.ts +5 -0
- package/dist/generators/tanstack-query/generator.js +11 -0
- package/dist/generators/use-async-data/generator.d.ts +5 -0
- package/dist/generators/use-async-data/generator.js +156 -0
- package/dist/generators/use-async-data/parser.d.ts +5 -0
- package/dist/generators/use-async-data/parser.js +5 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.d.ts +38 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.js +122 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.d.ts +54 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +126 -0
- package/dist/generators/use-async-data/templates.d.ts +20 -0
- package/dist/generators/use-async-data/templates.js +191 -0
- package/dist/generators/use-async-data/types.d.ts +4 -0
- package/dist/generators/use-async-data/types.js +4 -0
- package/dist/generators/use-fetch/generator.d.ts +5 -0
- package/dist/generators/use-fetch/generator.js +131 -0
- package/dist/generators/use-fetch/parser.d.ts +9 -0
- package/dist/generators/use-fetch/parser.js +282 -0
- package/dist/generators/use-fetch/runtime/useApiRequest.d.ts +46 -0
- package/dist/generators/use-fetch/runtime/useApiRequest.js +158 -0
- package/dist/generators/use-fetch/templates.d.ts +16 -0
- package/dist/generators/use-fetch/templates.js +169 -0
- package/dist/generators/use-fetch/types.d.ts +5 -0
- package/dist/generators/use-fetch/types.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +213 -0
- package/docs/API-REFERENCE.md +887 -0
- package/docs/ARCHITECTURE.md +649 -0
- package/docs/DEVELOPMENT.md +918 -0
- package/docs/QUICK-START.md +323 -0
- package/docs/README.md +155 -0
- package/docs/TROUBLESHOOTING.md +881 -0
- package/eslint.config.js +72 -0
- package/package.json +65 -0
- package/src/cli/config.ts +140 -0
- package/src/cli/logger.ts +66 -0
- package/src/cli/logo.ts +25 -0
- package/src/cli/messages.ts +97 -0
- package/src/cli/prompts.ts +143 -0
- package/src/cli/types.ts +50 -0
- package/src/cli/utils.ts +49 -0
- package/src/generate.ts +57 -0
- package/src/generators/nuxt-server/bff-templates.ts +754 -0
- package/src/generators/nuxt-server/generator.ts +270 -0
- package/src/generators/nuxt-server/parser.ts +5 -0
- package/src/generators/nuxt-server/templates.ts +483 -0
- package/src/generators/nuxt-server/types.ts +5 -0
- package/src/generators/shared/parsers/heyapi-parser.ts +307 -0
- package/src/generators/shared/parsers/official-parser.ts +5 -0
- package/src/generators/shared/runtime/apiHelpers.ts +466 -0
- package/src/generators/shared/templates/api-callbacks-plugin.ts +352 -0
- package/src/generators/shared/types.ts +27 -0
- package/src/generators/tanstack-query/generator.ts +11 -0
- package/src/generators/use-async-data/generator.ts +204 -0
- package/src/generators/use-async-data/parser.ts +5 -0
- package/src/generators/use-async-data/runtime/useApiAsyncData.ts +220 -0
- package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +236 -0
- package/src/generators/use-async-data/templates.ts +250 -0
- package/src/generators/use-async-data/types.ts +4 -0
- package/src/generators/use-fetch/generator.ts +169 -0
- package/src/generators/use-fetch/parser.ts +341 -0
- package/src/generators/use-fetch/runtime/useApiRequest.ts +223 -0
- package/src/generators/use-fetch/templates.ts +214 -0
- package/src/generators/use-fetch/types.ts +5 -0
- package/src/index.ts +265 -0
- 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,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,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,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;
|