fork of hey-api/openapi-ts because I need some additional things
1import fs from 'node:fs';
2import path from 'node:path';
3
4import type { UserConfig } from '@hey-api/openapi-ts';
5import { createClient } from '@hey-api/openapi-ts';
6import { addImportsSources, defineNuxtModule, useNuxt } from '@nuxt/kit';
7import type {} from '@nuxt/schema';
8import { defu } from 'defu';
9import { findExports, findTypeExports } from 'mlly';
10import { findPath } from 'nuxt/kit';
11
12interface ModuleOptions {
13 /**
14 * Module alias.
15 *
16 * @default '#hey-api'
17 */
18 alias?: string;
19 /**
20 * Automatically import all re-exported identifiers from the index file?
21 * You may want to disable this option if the imported identifier names
22 * clash with identifier names from other modules.
23 *
24 * @default true
25 */
26 autoImport?: boolean;
27 /**
28 * `@hey-api/openapi-ts` configuration options.
29 */
30 config: Omit<UserConfig, 'output'> & Partial<Pick<UserConfig, 'output'>>;
31}
32
33export default defineNuxtModule<ModuleOptions>({
34 defaults: {
35 alias: '#hey-api',
36 autoImport: true,
37 },
38 meta: {
39 configKey: 'heyApi',
40 name: '@hey-api/nuxt',
41 },
42 async setup(options) {
43 const nuxt = useNuxt();
44
45 const config = defu(options.config, {
46 output: {
47 path: path.join(nuxt.options.buildDir, 'client'),
48 },
49 plugins: (options.config.plugins || []).some(
50 (plugin: Required<UserConfig>['plugins'][number]) => {
51 const pluginName = typeof plugin === 'string' ? plugin : plugin.name;
52 return pluginName === '@hey-api/client-nuxt';
53 },
54 )
55 ? []
56 : ['@hey-api/client-nuxt'],
57 } satisfies Partial<UserConfig>) as UserConfig;
58
59 if (nuxt.options._prepare) {
60 config.watch = false;
61 }
62
63 const output = config.output instanceof Array ? config.output[0] : config.output;
64 const folder = path.resolve(
65 nuxt.options.rootDir,
66 typeof output === 'string' ? output : (output?.path ?? ''),
67 );
68
69 nuxt.options.alias[options.alias!] = folder;
70
71 nuxt.hooks.hookOnce('app:templates', async () => {
72 await createClient(config);
73 });
74
75 // auto-import enabled
76 if (options.autoImport) {
77 await createClient(config);
78 const typeImports = new Set<string>();
79 const valueImports = new Set<string>();
80 const files = findExports(fs.readFileSync(path.join(folder, 'index.ts'), 'utf-8'));
81 for (const file of files) {
82 if (!file.specifier || !/^\.{1,2}\//.test(file.specifier)) {
83 continue;
84 }
85 const filePath = await findPath(path.resolve(folder, file.specifier));
86 if (!filePath) {
87 continue;
88 }
89 const blob = fs.readFileSync(filePath, 'utf-8');
90 for (const { names } of findTypeExports(blob)) {
91 for (const name of names) {
92 typeImports.add(name);
93 }
94 }
95 for (const { names } of findExports(blob)) {
96 for (const name of names) {
97 valueImports.add(name);
98 }
99 }
100 }
101
102 const imports = [...[...typeImports].map((name) => ({ name, type: true })), ...valueImports];
103
104 if (imports.length && options.alias) {
105 addImportsSources({
106 from: options.alias,
107 imports,
108 });
109 }
110 }
111 },
112}) as any;