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,270 @@
1
+ import * as path from 'path';
2
+ import fs from 'fs-extra';
3
+ import { format } from 'prettier';
4
+ import {
5
+ getApiFiles as getApiFilesOfficial,
6
+ parseApiFile as parseApiFileOfficial,
7
+ } from './parser.js';
8
+ import {
9
+ getApiFiles as getApiFilesHeyApi,
10
+ parseApiFile as parseApiFileHeyApi,
11
+ } from '../shared/parsers/heyapi-parser.js';
12
+ import {
13
+ generateServerRouteFile,
14
+ generateRouteFilePath,
15
+ generateRoutesIndexFile,
16
+ } from './templates.js';
17
+ import {
18
+ generateAuthContextStub,
19
+ generateAuthTypesStub,
20
+ generateTransformerStub,
21
+ generateTransformerExamples,
22
+ generateBffReadme,
23
+ } from './bff-templates.js';
24
+ import type { MethodInfo } from './types.js';
25
+ import { p, logSuccess, logError, logNote } from '../../cli/logger.js';
26
+
27
+ /**
28
+ * Main function to generate Nuxt Server Routes
29
+ */
30
+ export async function generateNuxtServerRoutes(
31
+ inputDir: string,
32
+ serverRoutePath: string,
33
+ options?: {
34
+ enableBff?: boolean;
35
+ backend?: string;
36
+ }
37
+ ): Promise<void> {
38
+ const mainSpinner = p.spinner();
39
+
40
+ // Select parser based on chosen backend
41
+ const getApiFiles = options?.backend === 'heyapi' ? getApiFilesHeyApi : getApiFilesOfficial;
42
+ const parseApiFile = options?.backend === 'heyapi' ? parseApiFileHeyApi : parseApiFileOfficial;
43
+
44
+ const enableBff = options?.enableBff ?? false;
45
+
46
+ if (enableBff) {
47
+ p.log.info('BFF Mode: Enabled (transformers + auth)');
48
+ }
49
+
50
+ // 1. Get all API files
51
+ mainSpinner.start('Scanning API files');
52
+ const apiFiles = getApiFiles(inputDir);
53
+ mainSpinner.stop(`Found ${apiFiles.length} API file(s)`);
54
+
55
+ if (apiFiles.length === 0) {
56
+ throw new Error('No API files found in the input directory');
57
+ }
58
+
59
+ // 2. Parse each API file
60
+ mainSpinner.start('Parsing API files');
61
+ const allMethods: MethodInfo[] = [];
62
+
63
+ for (const file of apiFiles) {
64
+ const fileName = path.basename(file);
65
+ try {
66
+ const apiInfo = parseApiFile(file);
67
+ allMethods.push(...apiInfo.methods);
68
+ } catch (error) {
69
+ logError(`Error parsing ${fileName}: ${String(error)}`);
70
+ }
71
+ }
72
+
73
+ mainSpinner.stop(`Found ${allMethods.length} routes to generate`);
74
+
75
+ if (allMethods.length === 0) {
76
+ p.log.warn('No methods found to generate');
77
+ return;
78
+ }
79
+
80
+ // 3. Clean and create output directory
81
+ mainSpinner.start('Preparing output directory');
82
+ await fs.emptyDir(serverRoutePath);
83
+ mainSpinner.stop('Output directory ready');
84
+
85
+ // 4. Generate BFF structure if enabled
86
+ if (enableBff) {
87
+ await generateBffStructure(allMethods, serverRoutePath, inputDir);
88
+ }
89
+
90
+ // 5. Calculate relative import path from server routes to APIs
91
+ const relativePath = calculateRelativeImportPath(serverRoutePath, inputDir);
92
+
93
+ // 6. Generate each server route
94
+ mainSpinner.start('Generating server routes');
95
+ let successCount = 0;
96
+ let errorCount = 0;
97
+
98
+ for (const method of allMethods) {
99
+ try {
100
+ // Extract resource name from path
101
+ const resource = extractResourceFromPath(method.path);
102
+
103
+ const code = generateServerRouteFile(method, relativePath, {
104
+ enableBff: enableBff,
105
+ resource: resource,
106
+ });
107
+ const formattedCode = await formatCode(code);
108
+ const routeFilePath = generateRouteFilePath(method);
109
+ const fullPath = path.join(serverRoutePath, routeFilePath);
110
+
111
+ // Ensure directory exists
112
+ await fs.ensureDir(path.dirname(fullPath));
113
+
114
+ await fs.writeFile(fullPath, formattedCode, 'utf-8');
115
+ successCount++;
116
+ } catch (error) {
117
+ logError(`Error generating ${method.path} [${method.httpMethod}]: ${String(error)}`);
118
+ errorCount++;
119
+ }
120
+ }
121
+ mainSpinner.stop(`Generated ${successCount} server routes`);
122
+
123
+ // 7. Generate configuration files
124
+ mainSpinner.start('Generating configuration files');
125
+
126
+ // Generate routes index (documentation)
127
+ const routesIndexCode = generateRoutesIndexFile(allMethods);
128
+ const formattedRoutesIndex = await formatCode(routesIndexCode);
129
+ await fs.writeFile(path.join(serverRoutePath, '_routes.ts'), formattedRoutesIndex, 'utf-8');
130
+ mainSpinner.stop('Configuration files generated');
131
+
132
+ // 8. Summary and Next Steps
133
+ if (errorCount > 0) {
134
+ p.log.warn(`Completed with ${errorCount} error(s)`);
135
+ }
136
+ logSuccess(`Generated ${successCount} server route(s) in ${serverRoutePath}`);
137
+
138
+ // Build next steps message
139
+ let nextSteps = '1. Configure API_BASE_URL and API_SECRET in your .env\n';
140
+ nextSteps += '2. Update nuxt.config.ts with runtimeConfig (add apiBaseUrl and apiSecret)';
141
+
142
+ if (enableBff) {
143
+ nextSteps += '\n3. Implement authentication in server/auth/context.ts';
144
+ nextSteps += '\n4. Add business logic to transformers in server/bff/transformers/';
145
+ nextSteps += '\n5. See server/bff/README.md for BFF documentation';
146
+ nextSteps += '\n6. Start your Nuxt dev server and test the routes';
147
+ } else {
148
+ nextSteps += '\n3. Start your Nuxt dev server and test the routes';
149
+ }
150
+
151
+ logNote(nextSteps, 'Next steps');
152
+ }
153
+
154
+ /**
155
+ * Calculate relative import path from server routes to APIs
156
+ */
157
+ function calculateRelativeImportPath(serverRoutePath: string, inputDir: string): string {
158
+ // Use Nuxt's ~ alias (project root) so the path is stable regardless of serverRoutePath depth
159
+ const projectRoot = process.cwd();
160
+ const relativeInputDir = path.relative(projectRoot, path.resolve(inputDir));
161
+ // Convert Windows paths to Unix-style
162
+ return '~/' + relativeInputDir.replace(/\\/g, '/');
163
+ }
164
+
165
+ /**
166
+ * Format code with Prettier
167
+ */
168
+ async function formatCode(code: string): Promise<string> {
169
+ try {
170
+ return await format(code, {
171
+ parser: 'typescript',
172
+ semi: false,
173
+ singleQuote: true,
174
+ trailingComma: 'es5',
175
+ printWidth: 100,
176
+ });
177
+ } catch {
178
+ p.log.warn('Prettier formatting failed, using unformatted code');
179
+ return code;
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Generate BFF structure (auth + transformers)
185
+ */
186
+ async function generateBffStructure(
187
+ allMethods: MethodInfo[],
188
+ serverRoutePath: string,
189
+ inputDir: string
190
+ ): Promise<void> {
191
+ const bffSpinner = p.spinner();
192
+ bffSpinner.start('Generating BFF structure (auth + transformers)');
193
+
194
+ const serverRoot = path.dirname(serverRoutePath);
195
+
196
+ // 1. Generate auth files (only if they don't exist)
197
+ const authDir = path.join(serverRoot, 'auth');
198
+ await fs.ensureDir(authDir);
199
+
200
+ const authContextPath = path.join(authDir, 'context.ts');
201
+ if (!fs.existsSync(authContextPath)) {
202
+ const authContextCode = generateAuthContextStub();
203
+ const formattedAuthContext = await formatCode(authContextCode);
204
+ await fs.writeFile(authContextPath, formattedAuthContext, 'utf-8');
205
+ }
206
+
207
+ const authTypesPath = path.join(authDir, 'types.ts');
208
+ if (!fs.existsSync(authTypesPath)) {
209
+ const authTypesCode = generateAuthTypesStub();
210
+ const formattedAuthTypes = await formatCode(authTypesCode);
211
+ await fs.writeFile(authTypesPath, formattedAuthTypes, 'utf-8');
212
+ }
213
+
214
+ // 2. Generate transformer stubs (only if they don't exist)
215
+ const bffDir = path.join(serverRoot, 'bff');
216
+ const transformersDir = path.join(bffDir, 'transformers');
217
+ await fs.ensureDir(transformersDir);
218
+
219
+ // Group methods by resource
220
+ const methodsByResource = new Map<string, MethodInfo[]>();
221
+ for (const method of allMethods) {
222
+ const resource = extractResourceFromPath(method.path);
223
+ if (!methodsByResource.has(resource)) {
224
+ methodsByResource.set(resource, []);
225
+ }
226
+ methodsByResource.get(resource)!.push(method);
227
+ }
228
+
229
+ // Generate transformer for each resource
230
+ for (const [resource, methods] of methodsByResource.entries()) {
231
+ const transformerPath = path.join(transformersDir, `${resource}.ts`);
232
+ if (!fs.existsSync(transformerPath)) {
233
+ const transformerCode = generateTransformerStub(resource, methods, inputDir);
234
+ const formattedTransformer = await formatCode(transformerCode);
235
+ await fs.writeFile(transformerPath, formattedTransformer, 'utf-8');
236
+ }
237
+ }
238
+
239
+ // 3. Generate examples file (always regenerated)
240
+ const examplesPath = path.join(bffDir, '_transformers.example.ts');
241
+ const examplesCode = generateTransformerExamples();
242
+ const formattedExamples = await formatCode(examplesCode);
243
+ await fs.writeFile(examplesPath, formattedExamples, 'utf-8');
244
+
245
+ // 4. Generate BFF README (always regenerated)
246
+ const bffReadmePath = path.join(bffDir, 'README.md');
247
+ const bffReadmeCode = generateBffReadme();
248
+ await fs.writeFile(bffReadmePath, bffReadmeCode, 'utf-8');
249
+
250
+ bffSpinner.stop('BFF structure generated');
251
+ }
252
+
253
+ /**
254
+ * Extract resource name from API path
255
+ * Examples:
256
+ * /pet -> pet
257
+ * /pet/{id} -> pet
258
+ * /store/inventory -> store
259
+ * /user/login -> user
260
+ */
261
+ function extractResourceFromPath(path: string): string {
262
+ // Remove leading slash
263
+ const cleanPath = path.startsWith('/') ? path.substring(1) : path;
264
+
265
+ // Get first segment
266
+ const firstSegment = cleanPath.split('/')[0];
267
+
268
+ // Remove any path params
269
+ return firstSegment.replace(/\{[^}]+\}/g, '').toLowerCase();
270
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Re-export all parser functionality from use-fetch
3
+ * The parsing logic is identical for all generators
4
+ */
5
+ export * from '../use-fetch/parser.js';