fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at feat/use-query-options 244 lines 7.6 kB view raw
1import fs from 'node:fs'; 2import path from 'node:path'; 3import { fileURLToPath } from 'node:url'; 4 5import type { IProject } from '@hey-api/codegen-core'; 6import type { DefinePlugin, OutputHeader } from '@hey-api/shared'; 7import { ensureDirSync, isEnvironment, outputHeaderToPrefix } from '@hey-api/shared'; 8 9import type { Config } from '../config/types'; 10import type { Client } from '../plugins/@hey-api/client-core/types'; 11import { getClientPlugin } from '../plugins/@hey-api/client-core/utils'; 12 13const __filename = fileURLToPath(import.meta.url); 14const __dirname = path.dirname(__filename); 15 16/** 17 * Returns paths to client bundle files based on execution context 18 */ 19function getClientBundlePaths(pluginName: string): { 20 clientPath: string; 21 corePath: string; 22} { 23 const clientName = pluginName.slice('@hey-api/client-'.length); 24 25 if (isEnvironment('development')) { 26 // Dev: source bundle folders at src/plugins/@hey-api/{client}/bundle 27 const pluginsDir = path.resolve(__dirname, '..', 'plugins', '@hey-api'); 28 return { 29 clientPath: path.resolve(pluginsDir, `client-${clientName}`, 'bundle'), 30 corePath: path.resolve(pluginsDir, 'client-core', 'bundle'), 31 }; 32 } 33 34 // Prod: copied to dist/clients/{clientName} 35 return { 36 clientPath: path.resolve(__dirname, 'clients', clientName), 37 corePath: path.resolve(__dirname, 'clients', 'core'), 38 }; 39} 40 41/** 42 * Returns absolute path to the client folder. This is hard-coded for now. 43 */ 44export function clientFolderAbsolutePath(config: Config): string { 45 const client = getClientPlugin(config); 46 47 if ('bundle' in client.config && client.config.bundle) { 48 // not proud of this one 49 const renamed: Map<string, string> | undefined = 50 // @ts-expect-error 51 config._FRAGILE_CLIENT_BUNDLE_RENAMED; 52 return path.resolve(config.output.path, 'client', `${renamed?.get('index') ?? 'index'}.py`); 53 } 54 55 return client.name; 56} 57 58/** 59 * Recursively copies files and directories. 60 * This is a PnP-compatible alternative to fs.cpSync that works with Yarn PnP's 61 * virtualized filesystem. 62 */ 63function copyRecursivePnP(src: string, dest: string): void { 64 const stat = fs.statSync(src); 65 66 if (stat.isDirectory()) { 67 if (!fs.existsSync(dest)) { 68 fs.mkdirSync(dest, { recursive: true }); 69 } 70 71 const files = fs.readdirSync(src); 72 for (const file of files) { 73 copyRecursivePnP(path.join(src, file), path.join(dest, file)); 74 } 75 } else { 76 const content = fs.readFileSync(src); 77 fs.writeFileSync(dest, content); 78 } 79} 80 81function renameFile({ 82 filePath, 83 project, 84 renamed, 85}: { 86 filePath: string; 87 project: IProject; 88 renamed: Map<string, string>; 89}): void { 90 const extension = path.extname(filePath); 91 const name = path.basename(filePath, extension); 92 const renamedName = project.fileName?.(name) || name; 93 if (renamedName !== name) { 94 const outputPath = path.dirname(filePath); 95 fs.renameSync(filePath, path.resolve(outputPath, `${renamedName}${extension}`)); 96 renamed.set(name, renamedName); 97 } 98} 99 100function replaceImports({ 101 filePath, 102 header, 103 renamed, 104}: { 105 filePath: string; 106 header?: string; 107 renamed: Map<string, string>; 108}): void { 109 let content = fs.readFileSync(filePath, 'utf8'); 110 111 // Dev mode: rewrite source bundle imports to match output structure 112 if (isEnvironment('development')) { 113 // ...client_core.bundle.foo -> ..core.foo 114 content = content.replace( 115 /from\s+(\.{3,})\.?client_core\.bundle\./g, 116 (match, dots) => `from ${dots === '...' ? '..' : dots.slice(0, -1)}core.`, 117 ); 118 // ...client_core.bundle (index import) -> ..core 119 content = content.replace( 120 /from\s+(\.{3,})\.?client_core\.bundle['"]?/g, 121 (match, dots) => `from ${dots === '...' ? '..' : dots.slice(0, -1)}core`, 122 ); 123 } 124 125 content = content.replace(/from\s+(.+?)\s+import/g, (match, importPath) => { 126 const cleanPath = importPath.replace(/^['"]|['"]$/g, ''); 127 if (!cleanPath.startsWith('.')) return match; 128 const leadingDots = cleanPath.match(/^\.+/)?.[0] || ''; 129 const moduleName = cleanPath.replace(/^\.+/, ''); 130 if (!moduleName) return match; 131 const replacedName = renamed.get(moduleName) ?? moduleName; 132 return `from ${leadingDots}${replacedName} import`; 133 }); 134 135 const fileHeader = header ?? ''; 136 137 content = `${fileHeader}${content}`; 138 139 fs.writeFileSync(filePath, content, 'utf8'); 140} 141 142/** 143 * Creates a `client` folder containing the same modules as the client package. 144 */ 145export function generateClientBundle({ 146 header, 147 outputPath, 148 plugin, 149 project, 150}: { 151 header?: OutputHeader; 152 outputPath: string; 153 plugin: DefinePlugin<Client.Config & { name: string }>['Config']; 154 project: IProject; 155}): Map<string, string> | undefined { 156 const renamed = new Map<string, string>(); 157 const headerPrefix = outputHeaderToPrefix({ 158 defaultValue: ['# This file is auto-generated by @hey-api/openapi-python'], 159 header, 160 project, 161 }); 162 163 // copy Hey API clients to output 164 const isHeyApiClientPlugin = plugin.name.startsWith('@hey-api/client-'); 165 if (isHeyApiClientPlugin) { 166 const { clientPath } = getClientBundlePaths(plugin.name); 167 // const { clientPath, corePath } = getClientBundlePaths(plugin.name); 168 169 // copy client core 170 // const coreOutputPath = path.resolve(outputPath, 'core'); 171 // ensureDirSync(coreOutputPath); 172 // copyRecursivePnP(corePath, coreOutputPath); 173 174 // copy client bundle 175 const clientOutputPath = path.resolve(outputPath, 'client'); 176 ensureDirSync(clientOutputPath); 177 copyRecursivePnP(clientPath, clientOutputPath); 178 179 if (project) { 180 // const copiedCoreFiles = fs.readdirSync(coreOutputPath); 181 // for (const file of copiedCoreFiles) { 182 // renameFile({ 183 // filePath: path.resolve(coreOutputPath, file), 184 // project, 185 // renamed, 186 // }); 187 // } 188 189 const copiedClientFiles = fs.readdirSync(clientOutputPath); 190 for (const file of copiedClientFiles) { 191 renameFile({ 192 filePath: path.resolve(clientOutputPath, file), 193 project, 194 renamed, 195 }); 196 } 197 } 198 199 // const coreFiles = fs.readdirSync(coreOutputPath); 200 // for (const file of coreFiles) { 201 // replaceImports({ 202 // filePath: path.resolve(coreOutputPath, file), 203 // renamed, 204 // }); 205 // } 206 207 const clientFiles = fs.readdirSync(clientOutputPath); 208 for (const file of clientFiles) { 209 replaceImports({ 210 filePath: path.resolve(clientOutputPath, file), 211 header: headerPrefix, 212 renamed, 213 }); 214 } 215 return renamed; 216 } 217 218 const clientSrcPath = path.isAbsolute(plugin.name) ? path.dirname(plugin.name) : undefined; 219 220 // copy custom local client to output 221 if (clientSrcPath) { 222 const dirPath = path.resolve(outputPath, 'client'); 223 ensureDirSync(dirPath); 224 copyRecursivePnP(clientSrcPath, dirPath); 225 return; 226 } 227 228 // copy third-party client to output 229 const clientModulePath = path.normalize(require.resolve(plugin.name)); 230 const clientModulePathComponents = clientModulePath.split(path.sep); 231 const clientDistPath = clientModulePathComponents 232 .slice(0, clientModulePathComponents.indexOf('dist') + 1) 233 .join(path.sep); 234 235 const indexJsFile = clientModulePathComponents[clientModulePathComponents.length - 1]; 236 const distFiles = [indexJsFile!, 'index.d.mts', 'index.d.cts']; 237 const dirPath = path.resolve(outputPath, 'client'); 238 ensureDirSync(dirPath); 239 for (const file of distFiles) { 240 fs.copyFileSync(path.resolve(clientDistPath, file), path.resolve(dirPath, file)); 241 } 242 243 return; 244}