nuxt-openapi-hyperfetch 0.2.7-alpha.1 → 0.2.8-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 -26
- package/.prettierignore +17 -17
- package/CONTRIBUTING.md +291 -291
- package/INSTRUCTIONS.md +327 -327
- package/LICENSE +202 -202
- package/README.md +231 -231
- package/dist/cli/config.d.ts +9 -2
- package/dist/cli/config.js +1 -1
- package/dist/cli/logo.js +5 -5
- package/dist/cli/messages.d.ts +1 -0
- package/dist/cli/messages.js +2 -0
- package/dist/cli/prompts.d.ts +5 -0
- package/dist/cli/prompts.js +12 -0
- package/dist/cli/types.d.ts +1 -1
- package/dist/generators/components/connector-generator/templates.js +12 -12
- package/dist/generators/use-async-data/templates.js +17 -17
- package/dist/generators/use-fetch/templates.js +14 -14
- package/dist/index.js +39 -27
- package/dist/module/index.js +19 -0
- package/dist/module/types.d.ts +7 -0
- package/docs/API-REFERENCE.md +886 -886
- package/docs/generated-components.md +615 -615
- package/docs/headless-composables-ui.md +569 -569
- package/eslint.config.js +85 -85
- package/package.json +1 -1
- package/src/cli/config.ts +147 -140
- package/src/cli/logger.ts +124 -124
- package/src/cli/logo.ts +25 -25
- package/src/cli/messages.ts +4 -0
- package/src/cli/prompts.ts +14 -1
- package/src/cli/types.ts +50 -50
- package/src/generators/components/connector-generator/generator.ts +138 -138
- package/src/generators/components/connector-generator/templates.ts +254 -254
- package/src/generators/components/connector-generator/types.ts +34 -34
- package/src/generators/components/schema-analyzer/index.ts +44 -44
- package/src/generators/components/schema-analyzer/intent-detector.ts +187 -187
- package/src/generators/components/schema-analyzer/openapi-reader.ts +96 -96
- package/src/generators/components/schema-analyzer/resource-grouper.ts +166 -166
- package/src/generators/components/schema-analyzer/schema-field-mapper.ts +268 -268
- package/src/generators/components/schema-analyzer/types.ts +177 -177
- package/src/generators/nuxt-server/generator.ts +272 -272
- package/src/generators/shared/runtime/apiHelpers.ts +535 -535
- package/src/generators/shared/runtime/pagination.ts +323 -323
- package/src/generators/shared/runtime/useDeleteConnector.ts +109 -109
- package/src/generators/shared/runtime/useDetailConnector.ts +64 -64
- package/src/generators/shared/runtime/useFormConnector.ts +139 -139
- package/src/generators/shared/runtime/useListConnector.ts +148 -148
- package/src/generators/shared/runtime/zod-error-merger.ts +119 -119
- package/src/generators/shared/templates/api-callbacks-plugin.ts +399 -399
- package/src/generators/shared/templates/api-pagination-plugin.ts +158 -158
- package/src/generators/use-async-data/generator.ts +205 -205
- package/src/generators/use-async-data/runtime/useApiAsyncData.ts +329 -329
- package/src/generators/use-async-data/runtime/useApiAsyncDataRaw.ts +324 -324
- package/src/generators/use-async-data/templates.ts +257 -257
- package/src/generators/use-fetch/generator.ts +170 -170
- package/src/generators/use-fetch/runtime/useApiRequest.ts +354 -354
- package/src/generators/use-fetch/templates.ts +214 -214
- package/src/index.ts +305 -303
- package/src/module/index.ts +158 -133
- package/src/module/types.ts +39 -31
|
@@ -1,257 +1,257 @@
|
|
|
1
|
-
import type { MethodInfo } from './types.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Generate file header with auto-generation warning
|
|
5
|
-
*/
|
|
6
|
-
function generateFileHeader(): string {
|
|
7
|
-
return `/**
|
|
8
|
-
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
9
|
-
*
|
|
10
|
-
* This file was automatically generated by nuxt-openapi-generator.
|
|
11
|
-
* Any manual changes will be overwritten on the next generation.
|
|
12
|
-
*
|
|
13
|
-
* @generated by nuxt-openapi-generator
|
|
14
|
-
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/* eslint-disable */
|
|
18
|
-
// @ts-nocheck
|
|
19
|
-
`;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Options for code generation
|
|
24
|
-
*/
|
|
25
|
-
export interface GenerateOptions {
|
|
26
|
-
baseUrl?: string;
|
|
27
|
-
backend?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Generate a useAsyncData composable function
|
|
32
|
-
*/
|
|
33
|
-
export function generateComposableFile(
|
|
34
|
-
method: MethodInfo,
|
|
35
|
-
apiImportPath: string,
|
|
36
|
-
options?: GenerateOptions
|
|
37
|
-
): string {
|
|
38
|
-
const header = generateFileHeader();
|
|
39
|
-
const imports = generateImports(method, apiImportPath, false);
|
|
40
|
-
const functionBody = generateFunctionBody(method, false, options);
|
|
41
|
-
|
|
42
|
-
return `${header}${imports}\n\n${functionBody}\n`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Generate a useAsyncData composable function (Raw version with headers)
|
|
47
|
-
*/
|
|
48
|
-
export function generateRawComposableFile(
|
|
49
|
-
method: MethodInfo,
|
|
50
|
-
apiImportPath: string,
|
|
51
|
-
options?: GenerateOptions
|
|
52
|
-
): string {
|
|
53
|
-
const header = generateFileHeader();
|
|
54
|
-
const imports = generateImports(method, apiImportPath, true);
|
|
55
|
-
const functionBody = generateFunctionBody(method, true, options);
|
|
56
|
-
|
|
57
|
-
return `${header}${imports}\n\n${functionBody}\n`;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Extract base type names from a type string
|
|
62
|
-
* Examples:
|
|
63
|
-
* Pet[] -> Pet
|
|
64
|
-
* Array<Pet> -> Pet
|
|
65
|
-
* Pet -> Pet
|
|
66
|
-
* { [key: string]: Pet } -> (empty, it's anonymous)
|
|
67
|
-
*/
|
|
68
|
-
function extractBaseTypes(type: string): string[] {
|
|
69
|
-
if (!type) {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Handle array syntax: Pet[]
|
|
74
|
-
const arrayMatch = type.match(/^(\w+)\[\]$/);
|
|
75
|
-
if (arrayMatch) {
|
|
76
|
-
return [arrayMatch[1]];
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Handle Array generic: Array<Pet>
|
|
80
|
-
const arrayGenericMatch = type.match(/^Array<(\w+)>$/);
|
|
81
|
-
if (arrayGenericMatch) {
|
|
82
|
-
return [arrayGenericMatch[1]];
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// If it's a simple named type (single word, PascalCase), include it
|
|
86
|
-
if (/^[A-Z][a-zA-Z0-9]*$/.test(type)) {
|
|
87
|
-
return [type];
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// For complex types, don't extract anything
|
|
91
|
-
return [];
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Generate import statements
|
|
96
|
-
*/
|
|
97
|
-
function generateImports(method: MethodInfo, apiImportPath: string, isRaw: boolean): string {
|
|
98
|
-
const typeNames = new Set<string>();
|
|
99
|
-
|
|
100
|
-
// Extract base types from request type
|
|
101
|
-
if (method.requestType) {
|
|
102
|
-
const extracted = extractBaseTypes(method.requestType);
|
|
103
|
-
extracted.forEach((t) => typeNames.add(t));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Extract base types from response type
|
|
107
|
-
if (method.responseType && method.responseType !== 'void') {
|
|
108
|
-
const extracted = extractBaseTypes(method.responseType);
|
|
109
|
-
extracted.forEach((t) => typeNames.add(t));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
let imports = '';
|
|
113
|
-
|
|
114
|
-
// Import types from API (only if we have named types to import)
|
|
115
|
-
if (typeNames.size > 0) {
|
|
116
|
-
imports += `import type { ${Array.from(typeNames).join(', ')} } from '${apiImportPath}';\n`;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Import runtime helper (normal or raw)
|
|
120
|
-
if (isRaw) {
|
|
121
|
-
imports += `import { useApiAsyncDataRaw, type ApiAsyncDataRawOptions, type RawResponse } from '../runtime/useApiAsyncDataRaw';`;
|
|
122
|
-
} else {
|
|
123
|
-
imports += `import { useApiAsyncData, type ApiAsyncDataOptions } from '../runtime/useApiAsyncData';`;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return imports;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Generate the composable function body
|
|
131
|
-
*/
|
|
132
|
-
function generateFunctionBody(
|
|
133
|
-
method: MethodInfo,
|
|
134
|
-
isRaw: boolean,
|
|
135
|
-
generateOptions?: GenerateOptions
|
|
136
|
-
): string {
|
|
137
|
-
const hasParams = !!method.requestType;
|
|
138
|
-
const paramsArg = hasParams ? `params: ${method.requestType}` : '';
|
|
139
|
-
|
|
140
|
-
// Determine the options type based on isRaw
|
|
141
|
-
const optionsType = isRaw
|
|
142
|
-
? `ApiAsyncDataRawOptions<${method.responseType}>`
|
|
143
|
-
: `ApiAsyncDataOptions<${method.responseType}>`;
|
|
144
|
-
const optionsArg = `options?: ${optionsType}`;
|
|
145
|
-
const args = hasParams ? `${paramsArg}, ${optionsArg}` : optionsArg;
|
|
146
|
-
|
|
147
|
-
// Determine the response type generic
|
|
148
|
-
const responseTypeGeneric = method.responseType !== 'void' ? `<${method.responseType}>` : '';
|
|
149
|
-
|
|
150
|
-
// Generate unique key for useAsyncData
|
|
151
|
-
const composableName =
|
|
152
|
-
isRaw && method.rawMethodName
|
|
153
|
-
? `useAsyncData${method.rawMethodName.replace(/Raw$/, '')}Raw`
|
|
154
|
-
: method.composableName.replace(/^useFetch/, 'useAsyncData');
|
|
155
|
-
|
|
156
|
-
const key = `'${composableName}'`;
|
|
157
|
-
|
|
158
|
-
const url = generateUrl(method);
|
|
159
|
-
const fetchOptions = generateFetchOptions(method, generateOptions);
|
|
160
|
-
|
|
161
|
-
const description = method.description ? `/**\n * ${method.description}\n */\n` : '';
|
|
162
|
-
|
|
163
|
-
// Choose the correct wrapper function
|
|
164
|
-
const wrapperFunction = isRaw ? 'useApiAsyncDataRaw' : 'useApiAsyncData';
|
|
165
|
-
|
|
166
|
-
const pInit = hasParams ? `\n const p = shallowRef(params)` : '';
|
|
167
|
-
|
|
168
|
-
const argsExtraction = hasParams
|
|
169
|
-
? ` const _hasKey = typeof args[0] === 'string'\n const params = _hasKey ? args[1] : args[0]\n const options = _hasKey ? { cacheKey: args[0], ...args[2] } : args[1]`
|
|
170
|
-
: ` const _hasKey = typeof args[0] === 'string'\n const options = _hasKey ? { cacheKey: args[0], ...args[1] } : args[0]`;
|
|
171
|
-
|
|
172
|
-
return `${description}export function ${composableName}(key: string, ${args})
|
|
173
|
-
export function ${composableName}(${args})
|
|
174
|
-
export function ${composableName}(...args: any[]) {
|
|
175
|
-
${argsExtraction}${pInit}
|
|
176
|
-
return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
|
|
177
|
-
}`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Generate URL (with path params if needed)
|
|
182
|
-
*/
|
|
183
|
-
function generateUrl(method: MethodInfo): string {
|
|
184
|
-
if (method.pathParams.length === 0) {
|
|
185
|
-
return `'${method.path}'`;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
let url = method.path;
|
|
189
|
-
for (const param of method.pathParams) {
|
|
190
|
-
const accessor = method.paramsShape === 'nested' ? `p.value.path.${param}` : `p.value.${param}`;
|
|
191
|
-
url = url.replace(`{${param}}`, `\${${accessor}}`);
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return `() => \`${url}\``;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Generate fetch options object
|
|
199
|
-
*/
|
|
200
|
-
function generateFetchOptions(method: MethodInfo, generateOptions?: GenerateOptions): string {
|
|
201
|
-
const options: string[] = [];
|
|
202
|
-
|
|
203
|
-
// Method
|
|
204
|
-
options.push(`method: '${method.httpMethod}'`);
|
|
205
|
-
|
|
206
|
-
// Base URL (if provided in config)
|
|
207
|
-
if (generateOptions?.baseUrl) {
|
|
208
|
-
options.push(`baseURL: '${generateOptions.baseUrl}'`);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// Body
|
|
212
|
-
if (method.hasBody) {
|
|
213
|
-
if (method.paramsShape === 'nested') {
|
|
214
|
-
options.push(`body: computed(() => p.value.body)`);
|
|
215
|
-
} else if (method.bodyField) {
|
|
216
|
-
options.push(`body: computed(() => p.value.${method.bodyField})`);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Query params (renamed to 'params' for $fetch)
|
|
221
|
-
if (method.hasQueryParams) {
|
|
222
|
-
if (method.paramsShape === 'nested') {
|
|
223
|
-
options.push(`params: computed(() => p.value.query)`);
|
|
224
|
-
} else if (method.queryParams.length > 0) {
|
|
225
|
-
const queryObj = method.queryParams
|
|
226
|
-
.map((param) => `${param}: p.value.${param}`)
|
|
227
|
-
.join(',\n ');
|
|
228
|
-
options.push(`params: computed(() => ({\n ${queryObj}\n }))`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Headers
|
|
233
|
-
if (Object.keys(method.headers).length > 0) {
|
|
234
|
-
const headersEntries = Object.entries(method.headers)
|
|
235
|
-
.map(([key, value]) => `'${key}': '${value}'`)
|
|
236
|
-
.join(',\n ');
|
|
237
|
-
options.push(`headers: {\n ${headersEntries},\n ...options?.headers\n }`);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Spread options
|
|
241
|
-
options.push('...options');
|
|
242
|
-
|
|
243
|
-
const optionsStr = options.join(',\n ');
|
|
244
|
-
return `{\n ${optionsStr}\n }`;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
* Generate index.ts that exports all composables
|
|
249
|
-
*/
|
|
250
|
-
export function generateIndexFile(composableNames: string[]): string {
|
|
251
|
-
const header = generateFileHeader();
|
|
252
|
-
const exports = composableNames
|
|
253
|
-
.map((name) => `export { ${name} } from './composables/${name}'`)
|
|
254
|
-
.join('\n');
|
|
255
|
-
|
|
256
|
-
return `${header}${exports}\n`;
|
|
257
|
-
}
|
|
1
|
+
import type { MethodInfo } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate file header with auto-generation warning
|
|
5
|
+
*/
|
|
6
|
+
function generateFileHeader(): string {
|
|
7
|
+
return `/**
|
|
8
|
+
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
9
|
+
*
|
|
10
|
+
* This file was automatically generated by nuxt-openapi-generator.
|
|
11
|
+
* Any manual changes will be overwritten on the next generation.
|
|
12
|
+
*
|
|
13
|
+
* @generated by nuxt-openapi-generator
|
|
14
|
+
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/* eslint-disable */
|
|
18
|
+
// @ts-nocheck
|
|
19
|
+
`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Options for code generation
|
|
24
|
+
*/
|
|
25
|
+
export interface GenerateOptions {
|
|
26
|
+
baseUrl?: string;
|
|
27
|
+
backend?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Generate a useAsyncData composable function
|
|
32
|
+
*/
|
|
33
|
+
export function generateComposableFile(
|
|
34
|
+
method: MethodInfo,
|
|
35
|
+
apiImportPath: string,
|
|
36
|
+
options?: GenerateOptions
|
|
37
|
+
): string {
|
|
38
|
+
const header = generateFileHeader();
|
|
39
|
+
const imports = generateImports(method, apiImportPath, false);
|
|
40
|
+
const functionBody = generateFunctionBody(method, false, options);
|
|
41
|
+
|
|
42
|
+
return `${header}${imports}\n\n${functionBody}\n`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Generate a useAsyncData composable function (Raw version with headers)
|
|
47
|
+
*/
|
|
48
|
+
export function generateRawComposableFile(
|
|
49
|
+
method: MethodInfo,
|
|
50
|
+
apiImportPath: string,
|
|
51
|
+
options?: GenerateOptions
|
|
52
|
+
): string {
|
|
53
|
+
const header = generateFileHeader();
|
|
54
|
+
const imports = generateImports(method, apiImportPath, true);
|
|
55
|
+
const functionBody = generateFunctionBody(method, true, options);
|
|
56
|
+
|
|
57
|
+
return `${header}${imports}\n\n${functionBody}\n`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extract base type names from a type string
|
|
62
|
+
* Examples:
|
|
63
|
+
* Pet[] -> Pet
|
|
64
|
+
* Array<Pet> -> Pet
|
|
65
|
+
* Pet -> Pet
|
|
66
|
+
* { [key: string]: Pet } -> (empty, it's anonymous)
|
|
67
|
+
*/
|
|
68
|
+
function extractBaseTypes(type: string): string[] {
|
|
69
|
+
if (!type) {
|
|
70
|
+
return [];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Handle array syntax: Pet[]
|
|
74
|
+
const arrayMatch = type.match(/^(\w+)\[\]$/);
|
|
75
|
+
if (arrayMatch) {
|
|
76
|
+
return [arrayMatch[1]];
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Handle Array generic: Array<Pet>
|
|
80
|
+
const arrayGenericMatch = type.match(/^Array<(\w+)>$/);
|
|
81
|
+
if (arrayGenericMatch) {
|
|
82
|
+
return [arrayGenericMatch[1]];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// If it's a simple named type (single word, PascalCase), include it
|
|
86
|
+
if (/^[A-Z][a-zA-Z0-9]*$/.test(type)) {
|
|
87
|
+
return [type];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// For complex types, don't extract anything
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generate import statements
|
|
96
|
+
*/
|
|
97
|
+
function generateImports(method: MethodInfo, apiImportPath: string, isRaw: boolean): string {
|
|
98
|
+
const typeNames = new Set<string>();
|
|
99
|
+
|
|
100
|
+
// Extract base types from request type
|
|
101
|
+
if (method.requestType) {
|
|
102
|
+
const extracted = extractBaseTypes(method.requestType);
|
|
103
|
+
extracted.forEach((t) => typeNames.add(t));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Extract base types from response type
|
|
107
|
+
if (method.responseType && method.responseType !== 'void') {
|
|
108
|
+
const extracted = extractBaseTypes(method.responseType);
|
|
109
|
+
extracted.forEach((t) => typeNames.add(t));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let imports = '';
|
|
113
|
+
|
|
114
|
+
// Import types from API (only if we have named types to import)
|
|
115
|
+
if (typeNames.size > 0) {
|
|
116
|
+
imports += `import type { ${Array.from(typeNames).join(', ')} } from '${apiImportPath}';\n`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Import runtime helper (normal or raw)
|
|
120
|
+
if (isRaw) {
|
|
121
|
+
imports += `import { useApiAsyncDataRaw, type ApiAsyncDataRawOptions, type RawResponse } from '../runtime/useApiAsyncDataRaw';`;
|
|
122
|
+
} else {
|
|
123
|
+
imports += `import { useApiAsyncData, type ApiAsyncDataOptions } from '../runtime/useApiAsyncData';`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return imports;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate the composable function body
|
|
131
|
+
*/
|
|
132
|
+
function generateFunctionBody(
|
|
133
|
+
method: MethodInfo,
|
|
134
|
+
isRaw: boolean,
|
|
135
|
+
generateOptions?: GenerateOptions
|
|
136
|
+
): string {
|
|
137
|
+
const hasParams = !!method.requestType;
|
|
138
|
+
const paramsArg = hasParams ? `params: ${method.requestType}` : '';
|
|
139
|
+
|
|
140
|
+
// Determine the options type based on isRaw
|
|
141
|
+
const optionsType = isRaw
|
|
142
|
+
? `ApiAsyncDataRawOptions<${method.responseType}>`
|
|
143
|
+
: `ApiAsyncDataOptions<${method.responseType}>`;
|
|
144
|
+
const optionsArg = `options?: ${optionsType}`;
|
|
145
|
+
const args = hasParams ? `${paramsArg}, ${optionsArg}` : optionsArg;
|
|
146
|
+
|
|
147
|
+
// Determine the response type generic
|
|
148
|
+
const responseTypeGeneric = method.responseType !== 'void' ? `<${method.responseType}>` : '';
|
|
149
|
+
|
|
150
|
+
// Generate unique key for useAsyncData
|
|
151
|
+
const composableName =
|
|
152
|
+
isRaw && method.rawMethodName
|
|
153
|
+
? `useAsyncData${method.rawMethodName.replace(/Raw$/, '')}Raw`
|
|
154
|
+
: method.composableName.replace(/^useFetch/, 'useAsyncData');
|
|
155
|
+
|
|
156
|
+
const key = `'${composableName}'`;
|
|
157
|
+
|
|
158
|
+
const url = generateUrl(method);
|
|
159
|
+
const fetchOptions = generateFetchOptions(method, generateOptions);
|
|
160
|
+
|
|
161
|
+
const description = method.description ? `/**\n * ${method.description}\n */\n` : '';
|
|
162
|
+
|
|
163
|
+
// Choose the correct wrapper function
|
|
164
|
+
const wrapperFunction = isRaw ? 'useApiAsyncDataRaw' : 'useApiAsyncData';
|
|
165
|
+
|
|
166
|
+
const pInit = hasParams ? `\n const p = shallowRef(params)` : '';
|
|
167
|
+
|
|
168
|
+
const argsExtraction = hasParams
|
|
169
|
+
? ` const _hasKey = typeof args[0] === 'string'\n const params = _hasKey ? args[1] : args[0]\n const options = _hasKey ? { cacheKey: args[0], ...args[2] } : args[1]`
|
|
170
|
+
: ` const _hasKey = typeof args[0] === 'string'\n const options = _hasKey ? { cacheKey: args[0], ...args[1] } : args[0]`;
|
|
171
|
+
|
|
172
|
+
return `${description}export function ${composableName}(key: string, ${args})
|
|
173
|
+
export function ${composableName}(${args})
|
|
174
|
+
export function ${composableName}(...args: any[]) {
|
|
175
|
+
${argsExtraction}${pInit}
|
|
176
|
+
return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
|
|
177
|
+
}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate URL (with path params if needed)
|
|
182
|
+
*/
|
|
183
|
+
function generateUrl(method: MethodInfo): string {
|
|
184
|
+
if (method.pathParams.length === 0) {
|
|
185
|
+
return `'${method.path}'`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let url = method.path;
|
|
189
|
+
for (const param of method.pathParams) {
|
|
190
|
+
const accessor = method.paramsShape === 'nested' ? `p.value.path.${param}` : `p.value.${param}`;
|
|
191
|
+
url = url.replace(`{${param}}`, `\${${accessor}}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return `() => \`${url}\``;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Generate fetch options object
|
|
199
|
+
*/
|
|
200
|
+
function generateFetchOptions(method: MethodInfo, generateOptions?: GenerateOptions): string {
|
|
201
|
+
const options: string[] = [];
|
|
202
|
+
|
|
203
|
+
// Method
|
|
204
|
+
options.push(`method: '${method.httpMethod}'`);
|
|
205
|
+
|
|
206
|
+
// Base URL (if provided in config)
|
|
207
|
+
if (generateOptions?.baseUrl) {
|
|
208
|
+
options.push(`baseURL: '${generateOptions.baseUrl}'`);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Body
|
|
212
|
+
if (method.hasBody) {
|
|
213
|
+
if (method.paramsShape === 'nested') {
|
|
214
|
+
options.push(`body: computed(() => p.value.body)`);
|
|
215
|
+
} else if (method.bodyField) {
|
|
216
|
+
options.push(`body: computed(() => p.value.${method.bodyField})`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Query params (renamed to 'params' for $fetch)
|
|
221
|
+
if (method.hasQueryParams) {
|
|
222
|
+
if (method.paramsShape === 'nested') {
|
|
223
|
+
options.push(`params: computed(() => p.value.query)`);
|
|
224
|
+
} else if (method.queryParams.length > 0) {
|
|
225
|
+
const queryObj = method.queryParams
|
|
226
|
+
.map((param) => `${param}: p.value.${param}`)
|
|
227
|
+
.join(',\n ');
|
|
228
|
+
options.push(`params: computed(() => ({\n ${queryObj}\n }))`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Headers
|
|
233
|
+
if (Object.keys(method.headers).length > 0) {
|
|
234
|
+
const headersEntries = Object.entries(method.headers)
|
|
235
|
+
.map(([key, value]) => `'${key}': '${value}'`)
|
|
236
|
+
.join(',\n ');
|
|
237
|
+
options.push(`headers: {\n ${headersEntries},\n ...options?.headers\n }`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Spread options
|
|
241
|
+
options.push('...options');
|
|
242
|
+
|
|
243
|
+
const optionsStr = options.join(',\n ');
|
|
244
|
+
return `{\n ${optionsStr}\n }`;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Generate index.ts that exports all composables
|
|
249
|
+
*/
|
|
250
|
+
export function generateIndexFile(composableNames: string[]): string {
|
|
251
|
+
const header = generateFileHeader();
|
|
252
|
+
const exports = composableNames
|
|
253
|
+
.map((name) => `export { ${name} } from './composables/${name}'`)
|
|
254
|
+
.join('\n');
|
|
255
|
+
|
|
256
|
+
return `${header}${exports}\n`;
|
|
257
|
+
}
|