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.

Merge pull request #3169 from hey-api/fix/core-symbol-merge

fix: simplify symbol merging logic

authored by

Lubos and committed by
GitHub
4eb6d1f2 24d8b038

+36 -68
+5
.changeset/warm-flowers-burn.md
··· 1 + --- 2 + '@hey-api/codegen-core': patch 3 + --- 4 + 5 + **fix**: simplify symbol merging logic
+2 -2
dev/openapi-ts.config.ts
··· 342 342 // signature: 'object', 343 343 // transformer: '@hey-api/transformers', 344 344 // transformer: true, 345 - validator: 'valibot', 345 + // validator: 'valibot', 346 346 // validator: { 347 347 // request: 'zod', 348 348 // response: 'zod', ··· 441 441 // definitions: 'z{{name}}', 442 442 exportFromIndex: true, 443 443 // metadata: true, 444 - name: 'valibot', 444 + // name: 'valibot', 445 445 // requests: { 446 446 // case: 'PascalCase', 447 447 // name: '{{name}}Data',
+3
packages/codegen-core/src/planner/planner.ts
··· 358 358 private assignTopLevelName( 359 359 args: Partial<AssignOptions> & { 360 360 ctx: AnalysisContext; 361 + debug?: boolean; 361 362 node?: INode; 362 363 symbol: Symbol; 363 364 }, ··· 388 389 args: Pick<Partial<AssignOptions>, 'scope'> & 389 390 Pick<AssignOptions, 'scopesToUpdate'> & { 390 391 ctx: AnalysisContext; 392 + debug?: boolean; 391 393 /** The file the symbol belongs to. */ 392 394 file: File; 393 395 node?: INode; ··· 410 412 private assignSymbolName( 411 413 args: AssignOptions & { 412 414 ctx: AnalysisContext; 415 + debug?: boolean; 413 416 /** The file the symbol belongs to. */ 414 417 file: File; 415 418 node?: INode;
+26 -66
packages/codegen-core/src/project/namespace.ts
··· 1 1 import type { SymbolKind } from '../symbols/types'; 2 2 3 + const kindRank: Record<SymbolKind, number> = { 4 + class: 3, 5 + enum: 4, 6 + function: 5, 7 + interface: 1, 8 + namespace: 0, 9 + type: 2, 10 + var: 6, 11 + }; 12 + 3 13 /** 4 14 * Returns true if two declarations of given kinds 5 15 * are allowed to share the same identifier in TypeScript. 6 16 */ 7 17 export function canShareName(a: SymbolKind, b: SymbolKind): boolean { 8 - // same-kind always valid for interfaces (merging) 9 - if (a === 'interface' && b === 'interface') return true; 10 - 11 - // type vs interface merges 12 - if ( 13 - (a === 'interface' && b === 'type') || 14 - (a === 'type' && b === 'interface') 15 - ) { 16 - return false; // TypeScript does NOT merge type-alias with interface. 17 - } 18 - 19 - // type vs type = conflict 20 - if (a === 'type' && b === 'type') return false; 21 - 22 - // interface vs class = allowed (declare-merge) 23 - if ( 24 - (a === 'interface' && b === 'class') || 25 - (a === 'class' && b === 'interface') 26 - ) { 27 - return true; 28 - } 29 - 30 - // enum vs namespace = allowed (merges into value+type) 31 - if ( 32 - (a === 'enum' && b === 'namespace') || 33 - (a === 'namespace' && b === 'enum') 34 - ) { 35 - return true; 36 - } 37 - 38 - // class vs namespace = allowed 39 - if ( 40 - (a === 'class' && b === 'namespace') || 41 - (a === 'namespace' && b === 'class') 42 - ) { 43 - return true; 18 + // sort based on TypeScript merge precedence so `a` is always the weaker merge candidate 19 + // ensures that asymmetric merges like `type + var` are correctly handled 20 + if (kindRank[a] > kindRank[b]) { 21 + [a, b] = [b, a]; 44 22 } 45 23 46 - // namespace vs namespace = allowed (merging) 47 - if (a === 'namespace' && b === 'namespace') return true; 48 - 49 - // enum vs enum = conflict IF values conflict (TypeScript flags duplicates) 50 - if (a === 'enum' && b === 'enum') return false; 51 - 52 - // function and namespace merge (namespace can augment function) 53 - if ( 54 - (a === 'function' && b === 'namespace') || 55 - (a === 'namespace' && b === 'function') 56 - ) { 57 - return true; 24 + switch (a) { 25 + case 'interface': 26 + return b === 'class' || b === 'interface'; 27 + case 'namespace': 28 + return ( 29 + b === 'class' || b === 'enum' || b === 'function' || b === 'namespace' 30 + ); 31 + case 'type': 32 + // type can only merge with value-only declarations 33 + return b === 'function' || b === 'var'; 34 + default: 35 + return false; 58 36 } 59 - 60 - // these collide with each other in the value namespace 61 - const valueKinds = new Set<SymbolKind>(['class', 'enum', 'function', 'var']); 62 - 63 - const aInValue = valueKinds.has(a); 64 - const bInValue = valueKinds.has(b); 65 - 66 - if (aInValue && bInValue) return false; 67 - 68 - // type-only declarations do not collide with value-only declarations 69 - const typeKinds = new Set<SymbolKind>(['interface', 'type']); 70 - const aInType = typeKinds.has(a); 71 - const bInType = typeKinds.has(b); 72 - 73 - // if one is type-only and the other is value-only, they do NOT collide 74 - if (aInType !== bInType) return true; 75 - 76 - return true; 77 37 }