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 323 lines 9.5 kB view raw
1import type { RenderContext, Renderer } from '@hey-api/codegen-core'; 2import type { BaseOutput } from '@hey-api/shared'; 3import type { MaybeArray, MaybeFunc } from '@hey-api/types'; 4 5import { py } from '../../py-compiler'; 6import type { PyDsl } from '../../py-dsl'; 7import type { ModuleExport, ModuleImport, SortGroup, SortKey, SortModule } from './render-utils'; 8import { astToString, moduleSortKey } from './render-utils'; 9 10type Exports = ReadonlyArray<ReadonlyArray<ModuleExport>>; 11type ExportsOptions = { 12 preferExportAll?: boolean; 13}; 14type Header = MaybeArray<string> | null | undefined; 15type HeaderArg = MaybeFunc<(ctx: RenderContext<PyDsl>) => Header>; 16type Imports = Array<ReadonlyArray<ModuleImport>>; 17 18function headerToLines(header: Header): ReadonlyArray<string> { 19 if (!header) return []; 20 const lines: Array<string> = []; 21 if (typeof header === 'string') { 22 lines.push(...header.split(/\r?\n/)); 23 return lines; 24 } 25 for (const line of header) { 26 lines.push(...line.split(/\r?\n/)); 27 } 28 return lines; 29} 30 31export class PythonRenderer implements Renderer { 32 /** 33 * Function to generate a file header. 34 * 35 * @private 36 */ 37 private _header?: HeaderArg; 38 /** 39 * Options for module specifier resolution. 40 * 41 * @private 42 */ 43 private _module?: Partial<BaseOutput>['module']; 44 /** 45 * Whether `export * from 'module'` should be used when possible instead of named exports. 46 * 47 * @private 48 */ 49 private _preferExportAll: boolean; 50 51 constructor( 52 args: Pick<Partial<BaseOutput>, 'module'> & { 53 header?: HeaderArg; 54 preferExportAll?: boolean; 55 } = {}, 56 ) { 57 this._header = args.header; 58 this._module = args.module; 59 this._preferExportAll = args.preferExportAll ?? false; 60 } 61 62 render(ctx: RenderContext<PyDsl>): string { 63 const header = typeof this._header === 'function' ? this._header(ctx) : this._header; 64 return PythonRenderer.astToString({ 65 exports: this.getExports(ctx), 66 exportsOptions: { 67 preferExportAll: this._preferExportAll, 68 }, 69 header, 70 imports: this.getImports(ctx), 71 nodes: ctx.file.nodes, 72 }); 73 } 74 75 supports(ctx: RenderContext): boolean { 76 return ctx.file.language === 'python'; 77 } 78 79 static astToString(args: { 80 exports?: Exports; 81 exportsOptions?: ExportsOptions; 82 header?: Header; 83 imports?: Imports; 84 nodes?: ReadonlyArray<PyDsl>; 85 /** 86 * Whether to include a trailing newline at the end of the file. 87 * 88 * @default true 89 */ 90 trailingNewline?: boolean; 91 }): string { 92 let text = ''; 93 for (const header of headerToLines(args.header)) { 94 text += `${header}\n`; 95 } 96 97 const argsImports = args.imports ?? []; 98 99 for (const group of args.exports ?? []) { 100 for (const exp of group) { 101 let found = false; 102 for (const impGroup of argsImports) { 103 if (found) break; 104 for (const imp of impGroup) { 105 if (imp.modulePath === exp.modulePath) { 106 // TODO: merge imports and exports from the same module 107 found = true; 108 break; 109 } 110 } 111 } 112 if (!found) { 113 argsImports.push([ 114 { 115 imports: exp.exports.map((exp) => ({ 116 isTypeOnly: exp.isTypeOnly, 117 localName: exp.exportedName, 118 sourceName: exp.exportedName, 119 })), 120 isTypeOnly: false, 121 kind: 'named', 122 modulePath: exp.modulePath, 123 }, 124 ]); 125 } 126 } 127 } 128 129 let imports = ''; 130 for (const group of argsImports) { 131 if (imports) imports += '\n'; 132 for (const imp of group) { 133 imports += `${astToString(PythonRenderer.toImportAst(imp))}`; 134 } 135 } 136 text = `${text}${text && imports ? '\n' : ''}${imports}`; 137 138 let exports = ''; 139 for (const group of args.exports ?? []) { 140 if (exports) exports += '\n'; 141 for (const exp of group) { 142 exports += `${astToString(PythonRenderer.toExportAst(exp, args.exportsOptions))}`; 143 } 144 } 145 text = `${text}${text && exports ? '\n' : ''}${exports}`; 146 147 let nodes = ''; 148 for (const node of args.nodes ?? []) { 149 if (nodes) nodes += '\n\n'; 150 nodes += `${astToString(node.toAst())}`; 151 } 152 text = `${text}${text && nodes ? '\n\n' : ''}${nodes}`; 153 154 if (args.trailingNewline === false && text.endsWith('\n')) { 155 text = text.slice(0, -1); 156 } 157 158 return text; 159 } 160 161 // eslint-disable-next-line @typescript-eslint/no-unused-vars 162 static toExportAst(group: ModuleExport, options?: ExportsOptions): py.Assignment { 163 const specifiers = group.exports.map((exp) => py.factory.createLiteral(exp.exportedName)); 164 return py.factory.createAssignment( 165 py.factory.createIdentifier('__all__'), 166 undefined, 167 py.factory.createListExpression(specifiers), 168 ); 169 } 170 171 static toImportAst(group: ModuleImport): py.ImportStatement { 172 const names: Array<{ 173 alias?: string; 174 name: string; 175 }> = group.imports.map((imp) => ({ 176 alias: imp.localName !== imp.sourceName ? imp.localName : undefined, 177 name: imp.sourceName, 178 })); 179 return py.factory.createImportStatement(group.modulePath, names, Boolean(group.imports.length)); 180 } 181 182 private getExports(ctx: RenderContext): Exports { 183 type ModuleEntry = { 184 group: ModuleExport; 185 sortKey: SortKey; 186 }; 187 188 const groups = new Map<SortGroup, Map<SortModule, ModuleEntry>>(); 189 190 for (const exp of ctx.file.exports) { 191 const sortKey = moduleSortKey({ 192 file: ctx.file, 193 fromFile: exp.from, 194 preferFileExtension: this._module?.extension || '', 195 root: ctx.project.root, 196 }); 197 const modulePath = this._module?.resolve?.(sortKey[2], ctx) ?? sortKey[2]; 198 const [groupIndex] = sortKey; 199 200 if (!groups.has(groupIndex)) groups.set(groupIndex, new Map()); 201 const moduleMap = groups.get(groupIndex)!; 202 203 if (!moduleMap.has(modulePath)) { 204 moduleMap.set(modulePath, { 205 group: { 206 canExportAll: exp.canExportAll, 207 exports: exp.exports, 208 isTypeOnly: exp.isTypeOnly, 209 modulePath, 210 namespaceExport: exp.namespaceExport, 211 }, 212 sortKey, 213 }); 214 } 215 } 216 217 const exports: Array<Array<ModuleExport>> = Array.from(groups.entries()) 218 .sort((a, b) => a[0] - b[0]) 219 .map(([, moduleMap]) => { 220 const entries = Array.from(moduleMap.values()); 221 222 entries.sort((a, b) => { 223 const d = a.sortKey[1] - b.sortKey[1]; 224 return d !== 0 ? d : a.group.modulePath.localeCompare(b.group.modulePath); 225 }); 226 227 return entries.map((e) => { 228 const group = e.group; 229 if (group.namespaceExport) { 230 group.exports = []; 231 } else { 232 const isTypeOnly = !group.exports.find((exp) => !exp.isTypeOnly); 233 if (isTypeOnly) { 234 group.isTypeOnly = true; 235 for (const exp of group.exports) { 236 exp.isTypeOnly = false; 237 } 238 } 239 group.exports.sort((a, b) => a.exportedName.localeCompare(b.exportedName)); 240 } 241 return group; 242 }); 243 }); 244 245 return exports; 246 } 247 248 private getImports(ctx: RenderContext): Imports { 249 type ModuleEntry = { 250 group: ModuleImport; 251 sortKey: SortKey; 252 }; 253 254 const groups = new Map<SortGroup, Map<SortModule, ModuleEntry>>(); 255 256 for (const imp of ctx.file.imports) { 257 const sortKey = moduleSortKey({ 258 file: ctx.file, 259 fromFile: imp.from, 260 preferFileExtension: this._module?.extension || '', 261 root: ctx.project.root, 262 }); 263 const modulePath = this._module?.resolve?.(sortKey[2], ctx) ?? sortKey[2]; 264 const [groupIndex] = sortKey; 265 266 if (!groups.has(groupIndex)) groups.set(groupIndex, new Map()); 267 const moduleMap = groups.get(groupIndex)!; 268 269 if (!moduleMap.has(modulePath)) { 270 moduleMap.set(modulePath, { 271 group: { 272 imports: [], 273 isTypeOnly: false, 274 kind: imp.kind, 275 modulePath, 276 }, 277 sortKey, 278 }); 279 } 280 281 const entry = moduleMap.get(modulePath)!; 282 const group = entry.group; 283 284 if (imp.kind !== 'named') { 285 group.isTypeOnly = imp.isTypeOnly; 286 group.kind = imp.kind; 287 group.localName = imp.localName; 288 } else { 289 group.imports.push(...imp.imports); 290 } 291 } 292 293 const imports: Array<Array<ModuleImport>> = Array.from(groups.entries()) 294 .sort((a, b) => a[0] - b[0]) 295 .map(([, moduleMap]) => { 296 const entries = Array.from(moduleMap.values()); 297 298 entries.sort((a, b) => { 299 const d = a.sortKey[1] - b.sortKey[1]; 300 return d !== 0 ? d : a.group.modulePath.localeCompare(b.group.modulePath); 301 }); 302 303 return entries.map((e) => { 304 const group = e.group; 305 if (group.kind === 'namespace') { 306 group.imports = []; 307 } else { 308 const isTypeOnly = !group.imports.find((imp) => !imp.isTypeOnly); 309 if (isTypeOnly) { 310 group.isTypeOnly = true; 311 for (const imp of group.imports) { 312 imp.isTypeOnly = false; 313 } 314 } 315 group.imports.sort((a, b) => a.localName.localeCompare(b.localName)); 316 } 317 return group; 318 }); 319 }); 320 321 return imports; 322 } 323}