nuxt-openapi-hyperfetch 0.2.7-alpha.1 → 0.3.0-beta
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 +309 -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 +68 -19
- package/dist/generators/shared/runtime/useFormConnector.js +8 -1
- package/dist/generators/shared/runtime/useListConnector.js +13 -6
- package/dist/generators/use-async-data/generator.js +4 -0
- package/dist/generators/use-async-data/runtime/useApiAsyncData.js +4 -4
- package/dist/generators/use-async-data/runtime/useApiAsyncDataRaw.js +4 -4
- package/dist/generators/use-async-data/templates.js +17 -17
- package/dist/generators/use-fetch/generator.js +4 -0
- package/dist/generators/use-fetch/templates.js +14 -14
- package/dist/index.js +40 -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 +307 -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 +147 -139
- package/src/generators/shared/runtime/useListConnector.ts +158 -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 +213 -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 +178 -170
- package/src/generators/use-fetch/runtime/useApiRequest.ts +354 -354
- package/src/generators/use-fetch/templates.ts +214 -214
- package/src/index.ts +306 -303
- package/src/module/index.ts +158 -133
- package/src/module/types.ts +39 -31
- package/dist/generators/tanstack-query/generator.d.ts +0 -5
- package/dist/generators/tanstack-query/generator.js +0 -11
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { pascalCase, kebabCase } from 'change-case';
|
|
2
2
|
// ─── File header ──────────────────────────────────────────────────────────────
|
|
3
3
|
function generateFileHeader() {
|
|
4
|
-
return `/**
|
|
5
|
-
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
6
|
-
*
|
|
7
|
-
* This file was automatically generated by nuxt-openapi-generator.
|
|
8
|
-
* Any manual changes will be overwritten on the next generation.
|
|
9
|
-
*
|
|
10
|
-
* @generated by nuxt-openapi-generator
|
|
11
|
-
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/* eslint-disable */
|
|
15
|
-
// @ts-nocheck
|
|
4
|
+
return `/**
|
|
5
|
+
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
6
|
+
*
|
|
7
|
+
* This file was automatically generated by nuxt-openapi-generator.
|
|
8
|
+
* Any manual changes will be overwritten on the next generation.
|
|
9
|
+
*
|
|
10
|
+
* @generated by nuxt-openapi-generator
|
|
11
|
+
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/* eslint-disable */
|
|
15
|
+
// @ts-nocheck
|
|
16
16
|
`;
|
|
17
17
|
}
|
|
18
18
|
// ─── Naming helpers ───────────────────────────────────────────────────────────
|
|
@@ -103,19 +103,59 @@ function buildZodSchemas(resource) {
|
|
|
103
103
|
}
|
|
104
104
|
return lines.join('\n');
|
|
105
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Build a const array with the column definitions inferred from the resource.
|
|
108
|
+
* Returns an empty string if the resource has no columns.
|
|
109
|
+
*/
|
|
110
|
+
function buildColumns(resource) {
|
|
111
|
+
if (!resource.columns || resource.columns.length === 0) {
|
|
112
|
+
return '';
|
|
113
|
+
}
|
|
114
|
+
const camel = resource.composableName
|
|
115
|
+
.replace(/^use/, '')
|
|
116
|
+
.replace(/Connector$/, '')
|
|
117
|
+
.replace(/^./, (c) => c.toLowerCase());
|
|
118
|
+
const varName = `${camel}Columns`;
|
|
119
|
+
const entries = resource.columns
|
|
120
|
+
.map((col) => ` { key: '${col.key}', label: '${col.label}', type: '${col.type}' }`)
|
|
121
|
+
.join(',\n');
|
|
122
|
+
return `const ${varName} = [\n${entries},\n];`;
|
|
123
|
+
}
|
|
106
124
|
/**
|
|
107
125
|
* Build the body of the exported connector function.
|
|
108
126
|
*/
|
|
109
127
|
function buildFunctionBody(resource) {
|
|
110
128
|
const pascal = pascalCase(resource.name);
|
|
129
|
+
const hasColumns = resource.columns && resource.columns.length > 0;
|
|
130
|
+
const camel = resource.composableName
|
|
131
|
+
.replace(/^use/, '')
|
|
132
|
+
.replace(/Connector$/, '')
|
|
133
|
+
.replace(/^./, (c) => c.toLowerCase());
|
|
134
|
+
const columnsVar = `${camel}Columns`;
|
|
111
135
|
const subConnectors = [];
|
|
136
|
+
// Destructure options param — only what's relevant for this resource
|
|
137
|
+
const optionKeys = [];
|
|
138
|
+
if (resource.listEndpoint && hasColumns) {
|
|
139
|
+
optionKeys.push('columnLabels', 'columnLabel');
|
|
140
|
+
}
|
|
141
|
+
if (resource.createEndpoint && resource.zodSchemas.create) {
|
|
142
|
+
optionKeys.push('createSchema');
|
|
143
|
+
}
|
|
144
|
+
if (resource.updateEndpoint && resource.zodSchemas.update) {
|
|
145
|
+
optionKeys.push('updateSchema');
|
|
146
|
+
}
|
|
147
|
+
const optionsDestructure = optionKeys.length > 0 ? ` const { ${optionKeys.join(', ')} } = options;\n` : '';
|
|
112
148
|
if (resource.listEndpoint) {
|
|
113
149
|
const fn = toAsyncDataName(resource.listEndpoint.operationId);
|
|
114
150
|
// paginated: true tells useListConnector to expose pagination helpers
|
|
115
151
|
// (goToPage, nextPage, prevPage, setPerPage, pagination ref).
|
|
116
152
|
// We set it whenever the spec declares a list endpoint that has a response schema,
|
|
117
153
|
// which is a reliable proxy for "this API returns structured data worth paginating".
|
|
118
|
-
const
|
|
154
|
+
const paginatedFlag = resource.listEndpoint.responseSchema ? 'paginated: true' : '';
|
|
155
|
+
const columnsArg = hasColumns ? `columns: ${columnsVar}` : '';
|
|
156
|
+
const labelArgs = hasColumns ? 'columnLabels, columnLabel' : '';
|
|
157
|
+
const allArgs = [paginatedFlag, columnsArg, labelArgs].filter(Boolean).join(', ');
|
|
158
|
+
const opts = allArgs ? `{ ${allArgs} }` : '{}';
|
|
119
159
|
subConnectors.push(` const table = useListConnector(${fn}, ${opts});`);
|
|
120
160
|
}
|
|
121
161
|
if (resource.detailEndpoint) {
|
|
@@ -124,7 +164,9 @@ function buildFunctionBody(resource) {
|
|
|
124
164
|
}
|
|
125
165
|
if (resource.createEndpoint) {
|
|
126
166
|
const fn = toAsyncDataName(resource.createEndpoint.operationId);
|
|
127
|
-
const schemaArg = resource.zodSchemas.create
|
|
167
|
+
const schemaArg = resource.zodSchemas.create
|
|
168
|
+
? `{ schema: ${pascal}CreateSchema, schemaOverride: createSchema }`
|
|
169
|
+
: '{}';
|
|
128
170
|
subConnectors.push(` const createForm = useFormConnector(${fn}, ${schemaArg});`);
|
|
129
171
|
}
|
|
130
172
|
if (resource.updateEndpoint) {
|
|
@@ -139,11 +181,11 @@ function buildFunctionBody(resource) {
|
|
|
139
181
|
let schemaArg = '{}';
|
|
140
182
|
if (resource.zodSchemas.update && hasDetail) {
|
|
141
183
|
// Best case: validate AND pre-fill from detail
|
|
142
|
-
schemaArg = `{ schema: ${pascal}UpdateSchema, loadWith: detail }`;
|
|
184
|
+
schemaArg = `{ schema: ${pascal}UpdateSchema, schemaOverride: updateSchema, loadWith: detail }`;
|
|
143
185
|
}
|
|
144
186
|
else if (resource.zodSchemas.update) {
|
|
145
187
|
// Validate, but no detail endpoint to pre-fill from
|
|
146
|
-
schemaArg = `{ schema: ${pascal}UpdateSchema }`;
|
|
188
|
+
schemaArg = `{ schema: ${pascal}UpdateSchema, schemaOverride: updateSchema }`;
|
|
147
189
|
}
|
|
148
190
|
else if (hasDetail) {
|
|
149
191
|
// No Zod schema (no request body in spec), but still pre-fill from detail
|
|
@@ -174,11 +216,14 @@ function buildFunctionBody(resource) {
|
|
|
174
216
|
}
|
|
175
217
|
const returnStatement = ` return { ${returnKeys.join(', ')} };`;
|
|
176
218
|
return [
|
|
177
|
-
`export function ${resource.composableName}() {`,
|
|
219
|
+
`export function ${resource.composableName}(options = {}) {`,
|
|
220
|
+
optionsDestructure.trimEnd(),
|
|
178
221
|
...subConnectors,
|
|
179
222
|
returnStatement,
|
|
180
223
|
`}`,
|
|
181
|
-
]
|
|
224
|
+
]
|
|
225
|
+
.filter((s) => s !== '')
|
|
226
|
+
.join('\n');
|
|
182
227
|
}
|
|
183
228
|
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
184
229
|
/**
|
|
@@ -192,14 +237,18 @@ export function generateConnectorFile(resource, composablesRelDir) {
|
|
|
192
237
|
const header = generateFileHeader();
|
|
193
238
|
const imports = buildImports(resource, composablesRelDir);
|
|
194
239
|
const schemas = buildZodSchemas(resource);
|
|
240
|
+
const columns = buildColumns(resource);
|
|
195
241
|
const fn = buildFunctionBody(resource);
|
|
196
|
-
// Assemble file: header + imports + (optional) Zod blocks + function body.
|
|
242
|
+
// Assemble file: header + imports + (optional) Zod blocks + columns const + function body.
|
|
197
243
|
// Each section ends with its own trailing newline; join with \n adds one blank
|
|
198
244
|
// line between sections, which matches Prettier's output for this structure.
|
|
199
245
|
const parts = [header, imports];
|
|
200
246
|
if (schemas.trim()) {
|
|
201
247
|
parts.push(schemas);
|
|
202
248
|
}
|
|
249
|
+
if (columns.trim()) {
|
|
250
|
+
parts.push(columns);
|
|
251
|
+
}
|
|
203
252
|
parts.push(fn);
|
|
204
253
|
return parts.join('\n') + '\n';
|
|
205
254
|
}
|
|
@@ -18,7 +18,14 @@ import { mergeZodErrors } from './zod-error-merger.js';
|
|
|
18
18
|
* @param options { schema, fields, loadWith?, errorConfig? }
|
|
19
19
|
*/
|
|
20
20
|
export function useFormConnector(composableFn, options = {}) {
|
|
21
|
-
const { schema, fields = [], loadWith = null, errorConfig = {} } = options;
|
|
21
|
+
const { schema: baseSchema, schemaOverride, fields = [], loadWith = null, errorConfig = {} } = options;
|
|
22
|
+
// Resolve the active schema:
|
|
23
|
+
// schemaOverride(base) — extend or refine the generated schema
|
|
24
|
+
// schemaOverride — replace the generated schema entirely
|
|
25
|
+
// baseSchema — the generated schema unchanged (default)
|
|
26
|
+
const schema = schemaOverride
|
|
27
|
+
? (typeof schemaOverride === 'function' ? schemaOverride(baseSchema) : schemaOverride)
|
|
28
|
+
: baseSchema;
|
|
22
29
|
// ── Form state ─────────────────────────────────────────────────────────────
|
|
23
30
|
const model = ref({});
|
|
24
31
|
const errors = ref({});
|
|
@@ -16,7 +16,7 @@ import { ref, computed, shallowRef } from 'vue';
|
|
|
16
16
|
* @param options Configuration for the list connector
|
|
17
17
|
*/
|
|
18
18
|
export function useListConnector(composableFn, options = {}) {
|
|
19
|
-
const { paginated = false, columns = [] } = options;
|
|
19
|
+
const { paginated = false, columns = [], columnLabels = {}, columnLabel = null } = options;
|
|
20
20
|
// ── Execute the underlying composable ──────────────────────────────────────
|
|
21
21
|
const composable = composableFn({ paginated });
|
|
22
22
|
// ── Derived state ──────────────────────────────────────────────────────────
|
|
@@ -36,16 +36,16 @@ export function useListConnector(composableFn, options = {}) {
|
|
|
36
36
|
// Pagination — passthrough from the underlying composable when paginated: true
|
|
37
37
|
const pagination = computed(() => composable.pagination?.value ?? null);
|
|
38
38
|
function goToPage(page) {
|
|
39
|
-
composable.goToPage?.(page);
|
|
39
|
+
composable.pagination?.value?.goToPage?.(page);
|
|
40
40
|
}
|
|
41
41
|
function nextPage() {
|
|
42
|
-
composable.nextPage?.();
|
|
42
|
+
composable.pagination?.value?.nextPage?.();
|
|
43
43
|
}
|
|
44
44
|
function prevPage() {
|
|
45
|
-
composable.prevPage?.();
|
|
45
|
+
composable.pagination?.value?.prevPage?.();
|
|
46
46
|
}
|
|
47
47
|
function setPerPage(n) {
|
|
48
|
-
composable.setPerPage?.(n);
|
|
48
|
+
composable.pagination?.value?.setPerPage?.(n);
|
|
49
49
|
}
|
|
50
50
|
// ── Row selection ──────────────────────────────────────────────────────────
|
|
51
51
|
const selected = ref([]);
|
|
@@ -95,10 +95,17 @@ export function useListConnector(composableFn, options = {}) {
|
|
|
95
95
|
function remove(row) {
|
|
96
96
|
_deleteTarget.value = row;
|
|
97
97
|
}
|
|
98
|
+
// Apply label overrides: columnLabel function takes priority over columnLabels map
|
|
99
|
+
const resolvedColumns = computed(() => columns.map((col) => ({
|
|
100
|
+
...col,
|
|
101
|
+
label: columnLabel
|
|
102
|
+
? columnLabel(col.key)
|
|
103
|
+
: (columnLabels[col.key] ?? col.label),
|
|
104
|
+
})));
|
|
98
105
|
return {
|
|
99
106
|
// State
|
|
100
107
|
rows,
|
|
101
|
-
columns:
|
|
108
|
+
columns: resolvedColumns,
|
|
102
109
|
loading,
|
|
103
110
|
error,
|
|
104
111
|
// Pagination
|
|
@@ -66,6 +66,10 @@ export async function generateUseAsyncDataComposables(inputDir, outputDir, optio
|
|
|
66
66
|
const sharedHelpersSource = path.resolve(__dirname, '../../../src/generators/shared/runtime/apiHelpers.ts');
|
|
67
67
|
const sharedHelpersDest = path.join(sharedRuntimeDir, 'apiHelpers.ts');
|
|
68
68
|
await fs.copyFile(sharedHelpersSource, sharedHelpersDest);
|
|
69
|
+
// Copy shared pagination.ts
|
|
70
|
+
const sharedPaginationSource = path.resolve(__dirname, '../../../src/generators/shared/runtime/pagination.ts');
|
|
71
|
+
const sharedPaginationDest = path.join(sharedRuntimeDir, 'pagination.ts');
|
|
72
|
+
await fs.copyFile(sharedPaginationSource, sharedPaginationDest);
|
|
69
73
|
mainSpinner.stop('Runtime files copied');
|
|
70
74
|
// 5. Calculate relative import path from composables to APIs
|
|
71
75
|
const relativePath = calculateRelativeImportPath(composablesDir, inputDir);
|
|
@@ -232,10 +232,10 @@ export function useApiAsyncData(key, url, options) {
|
|
|
232
232
|
...paginationState.value,
|
|
233
233
|
hasNextPage: hasNextPage.value,
|
|
234
234
|
hasPrevPage: hasPrevPage.value,
|
|
235
|
+
goToPage,
|
|
236
|
+
nextPage,
|
|
237
|
+
prevPage,
|
|
238
|
+
setPerPage,
|
|
235
239
|
})),
|
|
236
|
-
goToPage,
|
|
237
|
-
nextPage,
|
|
238
|
-
prevPage,
|
|
239
|
-
setPerPage,
|
|
240
240
|
};
|
|
241
241
|
}
|
|
@@ -213,10 +213,10 @@ export function useApiAsyncDataRaw(key, url, options) {
|
|
|
213
213
|
...paginationState.value,
|
|
214
214
|
hasNextPage: hasNextPage.value,
|
|
215
215
|
hasPrevPage: hasPrevPage.value,
|
|
216
|
+
goToPage,
|
|
217
|
+
nextPage,
|
|
218
|
+
prevPage,
|
|
219
|
+
setPerPage,
|
|
216
220
|
})),
|
|
217
|
-
goToPage,
|
|
218
|
-
nextPage,
|
|
219
|
-
prevPage,
|
|
220
|
-
setPerPage,
|
|
221
221
|
};
|
|
222
222
|
}
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* Generate file header with auto-generation warning
|
|
3
3
|
*/
|
|
4
4
|
function generateFileHeader() {
|
|
5
|
-
return `/**
|
|
6
|
-
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
7
|
-
*
|
|
8
|
-
* This file was automatically generated by nuxt-openapi-generator.
|
|
9
|
-
* Any manual changes will be overwritten on the next generation.
|
|
10
|
-
*
|
|
11
|
-
* @generated by nuxt-openapi-generator
|
|
12
|
-
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/* eslint-disable */
|
|
16
|
-
// @ts-nocheck
|
|
5
|
+
return `/**
|
|
6
|
+
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
7
|
+
*
|
|
8
|
+
* This file was automatically generated by nuxt-openapi-generator.
|
|
9
|
+
* Any manual changes will be overwritten on the next generation.
|
|
10
|
+
*
|
|
11
|
+
* @generated by nuxt-openapi-generator
|
|
12
|
+
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* eslint-disable */
|
|
16
|
+
// @ts-nocheck
|
|
17
17
|
`;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
@@ -120,11 +120,11 @@ function generateFunctionBody(method, isRaw, generateOptions) {
|
|
|
120
120
|
const argsExtraction = hasParams
|
|
121
121
|
? ` 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]`
|
|
122
122
|
: ` const _hasKey = typeof args[0] === 'string'\n const options = _hasKey ? { cacheKey: args[0], ...args[1] } : args[0]`;
|
|
123
|
-
return `${description}export function ${composableName}(key: string, ${args})
|
|
124
|
-
export function ${composableName}(${args})
|
|
125
|
-
export function ${composableName}(...args: any[]) {
|
|
126
|
-
${argsExtraction}${pInit}
|
|
127
|
-
return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
|
|
123
|
+
return `${description}export function ${composableName}(key: string, ${args})
|
|
124
|
+
export function ${composableName}(${args})
|
|
125
|
+
export function ${composableName}(...args: any[]) {
|
|
126
|
+
${argsExtraction}${pInit}
|
|
127
|
+
return ${wrapperFunction}${responseTypeGeneric}(${key}, ${url}, ${fetchOptions})
|
|
128
128
|
}`;
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
@@ -62,6 +62,10 @@ export async function generateUseFetchComposables(inputDir, outputDir, options,
|
|
|
62
62
|
const sharedHelpersSource = path.resolve(__dirname, '../../../src/generators/shared/runtime/apiHelpers.ts');
|
|
63
63
|
const sharedHelpersDest = path.join(sharedRuntimeDir, 'apiHelpers.ts');
|
|
64
64
|
await fs.copyFile(sharedHelpersSource, sharedHelpersDest);
|
|
65
|
+
// Copy shared pagination.ts
|
|
66
|
+
const sharedPaginationSource = path.resolve(__dirname, '../../../src/generators/shared/runtime/pagination.ts');
|
|
67
|
+
const sharedPaginationDest = path.join(sharedRuntimeDir, 'pagination.ts');
|
|
68
|
+
await fs.copyFile(sharedPaginationSource, sharedPaginationDest);
|
|
65
69
|
mainSpinner.stop('Runtime files copied');
|
|
66
70
|
// 5. Calculate relative import path from composables to APIs
|
|
67
71
|
const relativePath = calculateRelativeImportPath(composablesDir, inputDir);
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
* Generate file header with auto-generation warning
|
|
3
3
|
*/
|
|
4
4
|
function generateFileHeader() {
|
|
5
|
-
return `/**
|
|
6
|
-
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
7
|
-
*
|
|
8
|
-
* This file was automatically generated by nuxt-openapi-generator.
|
|
9
|
-
* Any manual changes will be overwritten on the next generation.
|
|
10
|
-
*
|
|
11
|
-
* @generated by nuxt-openapi-generator
|
|
12
|
-
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/* eslint-disable */
|
|
16
|
-
// @ts-nocheck
|
|
5
|
+
return `/**
|
|
6
|
+
* ⚠️ AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
|
|
7
|
+
*
|
|
8
|
+
* This file was automatically generated by nuxt-openapi-generator.
|
|
9
|
+
* Any manual changes will be overwritten on the next generation.
|
|
10
|
+
*
|
|
11
|
+
* @generated by nuxt-openapi-generator
|
|
12
|
+
* @see https://github.com/dmartindiaz/nuxt-openapi-hyperfetch
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/* eslint-disable */
|
|
16
|
+
// @ts-nocheck
|
|
17
17
|
`;
|
|
18
18
|
}
|
|
19
19
|
/**
|
|
@@ -95,8 +95,8 @@ function generateFunctionBody(method, options) {
|
|
|
95
95
|
const fetchOptions = generateFetchOptions(method, options);
|
|
96
96
|
const description = method.description ? `/**\n * ${method.description}\n */\n` : '';
|
|
97
97
|
const pInit = hasParams ? `\n const p = shallowRef(params)` : '';
|
|
98
|
-
return `${description}export const ${method.composableName} = (${args}) => {${pInit}
|
|
99
|
-
return useApiRequest${responseTypeGeneric}(${url}, ${fetchOptions})
|
|
98
|
+
return `${description}export const ${method.composableName} = (${args}) => {${pInit}
|
|
99
|
+
return useApiRequest${responseTypeGeneric}(${url}, ${fetchOptions})
|
|
100
100
|
}`;
|
|
101
101
|
}
|
|
102
102
|
/**
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { generateUseFetchComposables } from './generators/use-fetch/generator.js
|
|
|
6
6
|
import { generateUseAsyncDataComposables } from './generators/use-async-data/generator.js';
|
|
7
7
|
import { generateNuxtServerRoutes } from './generators/nuxt-server/generator.js';
|
|
8
8
|
import { generateConnectors } from './generators/components/connector-generator/generator.js';
|
|
9
|
-
import { promptInitialInputs, promptInputPath, promptComposablesSelection, promptServerRoutePath, promptBffConfig, promptGeneratorBackend, } from './cli/prompts.js';
|
|
9
|
+
import { promptInitialInputs, promptInputPath, promptComposablesSelection, promptServerRoutePath, promptBffConfig, promptGeneratorBackend, promptConnectors, } from './cli/prompts.js';
|
|
10
10
|
import { MESSAGES } from './cli/messages.js';
|
|
11
11
|
import { displayLogo } from './cli/logo.js';
|
|
12
12
|
import { loadConfig, mergeConfig, parseTags, parseGenerators } from './cli/config.js';
|
|
@@ -26,6 +26,7 @@ program
|
|
|
26
26
|
.option('-v, --verbose', 'Enable verbose logging', false)
|
|
27
27
|
.option('--watch', 'Watch mode - regenerate on file changes', false)
|
|
28
28
|
.option('--generators <types>', 'Generators to use: useFetch,useAsyncData,nuxtServer')
|
|
29
|
+
.option('--connectors', 'Generate headless UI connectors on top of useAsyncData', false)
|
|
29
30
|
.option('--server-route-path <path>', 'Server route path (for nuxtServer mode)')
|
|
30
31
|
.option('--enable-bff', 'Enable BFF pattern (for nuxtServer mode)', false)
|
|
31
32
|
.option('--backend <type>', 'Generator backend: official (Java) or heyapi (Node.js)')
|
|
@@ -54,6 +55,8 @@ program
|
|
|
54
55
|
backend: options.backend === 'official' || options.backend === 'heyapi'
|
|
55
56
|
? options.backend
|
|
56
57
|
: undefined,
|
|
58
|
+
// Only propagate if explicitly passed — undefined means "ask the user"
|
|
59
|
+
createUseAsyncDataConnectors: options.connectors === true ? true : undefined,
|
|
57
60
|
});
|
|
58
61
|
if (config.verbose) {
|
|
59
62
|
console.log('Configuration:', config);
|
|
@@ -85,7 +88,8 @@ program
|
|
|
85
88
|
// 1. Determine composables to generate FIRST
|
|
86
89
|
let composables;
|
|
87
90
|
if (config.generators) {
|
|
88
|
-
|
|
91
|
+
// filter out 'connectors' — handled separately below
|
|
92
|
+
composables = config.generators.filter((g) => g !== 'connectors');
|
|
89
93
|
if (config.verbose) {
|
|
90
94
|
console.log(`Using generators from config: ${composables.join(', ')}`);
|
|
91
95
|
}
|
|
@@ -114,7 +118,17 @@ program
|
|
|
114
118
|
inputPath = await promptInputPath(config.input);
|
|
115
119
|
outputPath = config.output ?? './swagger';
|
|
116
120
|
}
|
|
117
|
-
// 3. Ask
|
|
121
|
+
// 3. Ask whether to generate headless connectors (only if useAsyncData selected)
|
|
122
|
+
let generateConnectorsFlag = false;
|
|
123
|
+
if (composables.includes('useAsyncData')) {
|
|
124
|
+
if (config.createUseAsyncDataConnectors !== undefined) {
|
|
125
|
+
generateConnectorsFlag = config.createUseAsyncDataConnectors;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
generateConnectorsFlag = await promptConnectors();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// 4. Ask for server route path if nuxtServer is selected
|
|
118
132
|
let serverRoutePath = config.serverRoutePath || '';
|
|
119
133
|
let enableBff = config.enableBff || false;
|
|
120
134
|
if (needsNuxtServer && !config.serverRoutePath) {
|
|
@@ -181,6 +195,29 @@ program
|
|
|
181
195
|
throw error;
|
|
182
196
|
}
|
|
183
197
|
}
|
|
198
|
+
// Generate headless connectors if requested (requires useAsyncData)
|
|
199
|
+
if (generateConnectorsFlag) {
|
|
200
|
+
const spinner = p.spinner();
|
|
201
|
+
spinner.start('Generating headless UI connectors...');
|
|
202
|
+
try {
|
|
203
|
+
if (!config.dryRun) {
|
|
204
|
+
await generateConnectors({
|
|
205
|
+
inputSpec: inputPath,
|
|
206
|
+
outputDir: `${composablesOutputDir}/connectors`,
|
|
207
|
+
composablesRelDir: '../use-async-data',
|
|
208
|
+
runtimeRelDir: '../../runtime',
|
|
209
|
+
});
|
|
210
|
+
spinner.stop('✓ Generated headless UI connectors');
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
spinner.stop('Would generate headless UI connectors (dry-run)');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
spinner.stop('✗ Failed to generate connectors');
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
184
221
|
if (config.dryRun) {
|
|
185
222
|
p.outro('🔍 Dry run complete - no files were modified');
|
|
186
223
|
}
|
|
@@ -211,28 +248,4 @@ program
|
|
|
211
248
|
process.exit(1);
|
|
212
249
|
}
|
|
213
250
|
});
|
|
214
|
-
program
|
|
215
|
-
.command('connectors')
|
|
216
|
-
.description('Generate headless connector composables from an OpenAPI spec')
|
|
217
|
-
.requiredOption('-i, --input <path>', 'Path to OpenAPI YAML or JSON spec')
|
|
218
|
-
.requiredOption('-o, --output <path>', 'Output directory for connector composables')
|
|
219
|
-
.option('--composables-dir <relPath>', 'Relative path from output dir to useAsyncData composables (default: ../use-async-data)')
|
|
220
|
-
.option('--runtime-dir <relPath>', 'Relative path from output dir where runtime helpers are copied (default: ../runtime)')
|
|
221
|
-
.action(async (options) => {
|
|
222
|
-
try {
|
|
223
|
-
displayLogo();
|
|
224
|
-
p.intro('Generating connector composables…');
|
|
225
|
-
await generateConnectors({
|
|
226
|
-
inputSpec: options.input,
|
|
227
|
-
outputDir: options.output,
|
|
228
|
-
composablesRelDir: options.composablesDir,
|
|
229
|
-
runtimeRelDir: options.runtimeDir,
|
|
230
|
-
});
|
|
231
|
-
p.outro('Done!');
|
|
232
|
-
}
|
|
233
|
-
catch (error) {
|
|
234
|
-
p.log.error(`Error: ${String(error)}`);
|
|
235
|
-
process.exit(1);
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
251
|
program.parse();
|
package/dist/module/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { checkJavaInstalled } from '../generate.js';
|
|
|
5
5
|
import { generateUseFetchComposables } from '../generators/use-fetch/generator.js';
|
|
6
6
|
import { generateUseAsyncDataComposables } from '../generators/use-async-data/generator.js';
|
|
7
7
|
import { generateNuxtServerRoutes } from '../generators/nuxt-server/generator.js';
|
|
8
|
+
import { generateConnectors } from '../generators/components/connector-generator/generator.js';
|
|
8
9
|
import { createConsoleLogger } from '../cli/logger.js';
|
|
9
10
|
export default defineNuxtModule({
|
|
10
11
|
meta: {
|
|
@@ -19,6 +20,7 @@ export default defineNuxtModule({
|
|
|
19
20
|
enableProductionBuild: true,
|
|
20
21
|
enableAutoGeneration: false,
|
|
21
22
|
enableAutoImport: true,
|
|
23
|
+
createUseAsyncDataConnectors: false,
|
|
22
24
|
},
|
|
23
25
|
setup(options, nuxt) {
|
|
24
26
|
// --- Guard: input is required ---
|
|
@@ -64,6 +66,20 @@ export default defineNuxtModule({
|
|
|
64
66
|
const serverRoutePath = path.resolve(nuxt.options.rootDir, options.serverRoutePath ?? 'server/routes/api');
|
|
65
67
|
await generateNuxtServerRoutes(resolvedOutput, serverRoutePath, { enableBff: options.enableBff, backend }, logger);
|
|
66
68
|
}
|
|
69
|
+
// 3. Generate headless connectors if requested (requires useAsyncData)
|
|
70
|
+
if (options.createUseAsyncDataConnectors &&
|
|
71
|
+
selectedGenerators.includes('useAsyncData')) {
|
|
72
|
+
const connectorsOutputDir = path.join(composablesOutputDir, 'connectors');
|
|
73
|
+
const runtimeDir = path.join(resolvedOutput, 'runtime');
|
|
74
|
+
await generateConnectors({
|
|
75
|
+
inputSpec: resolvedInput,
|
|
76
|
+
outputDir: connectorsOutputDir,
|
|
77
|
+
composablesRelDir: '../use-async-data',
|
|
78
|
+
runtimeRelDir: '../../runtime',
|
|
79
|
+
}, logger);
|
|
80
|
+
// Register #nxh alias so generated connector imports resolve
|
|
81
|
+
nuxt.options.alias['#nxh'] = runtimeDir;
|
|
82
|
+
}
|
|
67
83
|
};
|
|
68
84
|
// --- Hooks: dev build / production build ---
|
|
69
85
|
const isDev = nuxt.options.dev;
|
|
@@ -88,6 +104,9 @@ export default defineNuxtModule({
|
|
|
88
104
|
if (selectedGenerators.includes('useAsyncData')) {
|
|
89
105
|
addImportsDir(path.join(composablesOutputDir, 'use-async-data', 'composables'));
|
|
90
106
|
}
|
|
107
|
+
if (options.createUseAsyncDataConnectors && selectedGenerators.includes('useAsyncData')) {
|
|
108
|
+
addImportsDir(path.join(composablesOutputDir, 'connectors'));
|
|
109
|
+
}
|
|
91
110
|
}
|
|
92
111
|
},
|
|
93
112
|
});
|
package/dist/module/types.d.ts
CHANGED
|
@@ -24,4 +24,11 @@ export interface ModuleOptions extends GeneratorConfig {
|
|
|
24
24
|
* @default true
|
|
25
25
|
*/
|
|
26
26
|
enableAutoImport?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Generate headless UI connector composables on top of useAsyncData.
|
|
29
|
+
* Connectors provide ready-made logic for tables, pagination, forms and delete actions.
|
|
30
|
+
* Requires useAsyncData to also be in generators.
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
createUseAsyncDataConnectors?: boolean;
|
|
27
34
|
}
|