From 3204c931653f2a173432023e9096c3a27c4a0649 Mon Sep 17 00:00:00 2001 From: Tristan Zander Date: Wed, 15 May 2024 14:29:11 -0400 Subject: [PATCH 1/2] fix: Properly resolve the name of a default export using a layer of indirection --- src/lib/models/types.ts | 28 +++++++++++++++++++ src/test/converter2/issues/gh2574/default.ts | 6 ++++ src/test/converter2/issues/gh2574/index.ts | 3 ++ .../converter2/issues/gh2574/reexported.ts | 1 + src/test/issues.c2.test.ts | 16 +++++++++++ 5 files changed, 54 insertions(+) create mode 100644 src/test/converter2/issues/gh2574/default.ts create mode 100644 src/test/converter2/issues/gh2574/index.ts create mode 100644 src/test/converter2/issues/gh2574/reexported.ts diff --git a/src/lib/models/types.ts b/src/lib/models/types.ts index be49d3a91..be93044d5 100644 --- a/src/lib/models/types.ts +++ b/src/lib/models/types.ts @@ -907,11 +907,39 @@ export class ReferenceType extends Type { return new ReferenceType(name, target, project, name); } + /** + * In certain cases, TypeScript returns the name `default` for the name of a type that is defined with + * the format `export default class` or `export default function`. This method checks for that case and returns the + * declaration of the export instead of the name `default`. + */ + private static getNameForDefaultExport( + symbol: ts.Symbol, + ): string | undefined { + if ( + symbol.name !== "default" || + symbol.valueDeclaration === undefined + ) { + return; + } + + const name = ts.getNameOfDeclaration(symbol.valueDeclaration); + + if (!name) { + return; + } + + if (ts.isIdentifier(name)) { + return name.text; + } + } + static createSymbolReference( symbol: ts.Symbol, context: Context, name?: string, ) { + name ??= this.getNameForDefaultExport(symbol); + const ref = new ReferenceType( name ?? symbol.name, new ReflectionSymbolId(symbol), diff --git a/src/test/converter2/issues/gh2574/default.ts b/src/test/converter2/issues/gh2574/default.ts new file mode 100644 index 000000000..196131c09 --- /dev/null +++ b/src/test/converter2/issues/gh2574/default.ts @@ -0,0 +1,6 @@ +/** + * Default export and class implementation + */ +export default class DefaultExport { + constructor() {} +} diff --git a/src/test/converter2/issues/gh2574/index.ts b/src/test/converter2/issues/gh2574/index.ts new file mode 100644 index 000000000..2fb7153e0 --- /dev/null +++ b/src/test/converter2/issues/gh2574/index.ts @@ -0,0 +1,3 @@ +import { Default } from "./reexported"; + +export function usesDefaultExport(param: Default) {} diff --git a/src/test/converter2/issues/gh2574/reexported.ts b/src/test/converter2/issues/gh2574/reexported.ts new file mode 100644 index 000000000..65fe0b80b --- /dev/null +++ b/src/test/converter2/issues/gh2574/reexported.ts @@ -0,0 +1 @@ +export { default as Default } from "./default"; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index c41b253d6..057b04767 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -15,6 +15,7 @@ import { ProjectReflection, QueryType, ReferenceReflection, + ReferenceType, ReflectionKind, ReflectionType, SignatureReflection, @@ -91,6 +92,21 @@ describe("Issue Tests", () => { ); }); + it("#2574", () => { + const project = convert(); + const usesDefaultExport = query(project, "usesDefaultExport"); + const sig = usesDefaultExport.signatures?.[0]; + ok(sig, "Missing signature for usesDefaultExport"); + const param = sig.parameters?.[0]; + ok(param, "Missing parameter"); + equal(param.name, "param", "Incorrect parameter name"); + const paramType = param.type as ReferenceType | undefined; + ok(paramType, "Parameter type is not a reference type or undefined"); + equal(paramType.type, "reference", "Parameter is not a reference type"); + equal(paramType.name, "DefaultExport", "Incorrect reference name"); + equal(paramType.qualifiedName, "default", "Incorrect qualified name"); + }); + it("#671", () => { const project = convert(); const toNumber = query(project, "toNumber"); From 33a34d6768510acd9ca143ec860dde87eadfc70e Mon Sep 17 00:00:00 2001 From: Tristan Zander Date: Thu, 16 May 2024 12:43:54 -0400 Subject: [PATCH 2/2] address comments and add another test case --- src/lib/converter/types.ts | 8 +++ src/lib/models/types.ts | 28 ---------- src/test/converter2/issues/gh2574/index.ts | 4 +- .../converter2/issues/gh2574/notDefault.ts | 3 ++ .../converter2/issues/gh2574/reexported.ts | 1 + src/test/issues.c2.test.ts | 52 +++++++++++++------ 6 files changed, 51 insertions(+), 45 deletions(-) create mode 100644 src/test/converter2/issues/gh2574/notDefault.ts diff --git a/src/lib/converter/types.ts b/src/lib/converter/types.ts index 01b88154f..1fbf9478e 100644 --- a/src/lib/converter/types.ts +++ b/src/lib/converter/types.ts @@ -743,9 +743,17 @@ const referenceConverter: TypeConverter< return ref; } + let name; + if (ts.isIdentifier(node.typeName)) { + name = node.typeName.text; + } else { + name = node.typeName.right.text; + } + const ref = ReferenceType.createSymbolReference( context.resolveAliasedSymbol(symbol), context, + name, ); if (type.flags & ts.TypeFlags.Substitution) { // NoInfer diff --git a/src/lib/models/types.ts b/src/lib/models/types.ts index be93044d5..be49d3a91 100644 --- a/src/lib/models/types.ts +++ b/src/lib/models/types.ts @@ -907,39 +907,11 @@ export class ReferenceType extends Type { return new ReferenceType(name, target, project, name); } - /** - * In certain cases, TypeScript returns the name `default` for the name of a type that is defined with - * the format `export default class` or `export default function`. This method checks for that case and returns the - * declaration of the export instead of the name `default`. - */ - private static getNameForDefaultExport( - symbol: ts.Symbol, - ): string | undefined { - if ( - symbol.name !== "default" || - symbol.valueDeclaration === undefined - ) { - return; - } - - const name = ts.getNameOfDeclaration(symbol.valueDeclaration); - - if (!name) { - return; - } - - if (ts.isIdentifier(name)) { - return name.text; - } - } - static createSymbolReference( symbol: ts.Symbol, context: Context, name?: string, ) { - name ??= this.getNameForDefaultExport(symbol); - const ref = new ReferenceType( name ?? symbol.name, new ReflectionSymbolId(symbol), diff --git a/src/test/converter2/issues/gh2574/index.ts b/src/test/converter2/issues/gh2574/index.ts index 2fb7153e0..5f521cb67 100644 --- a/src/test/converter2/issues/gh2574/index.ts +++ b/src/test/converter2/issues/gh2574/index.ts @@ -1,3 +1,5 @@ -import { Default } from "./reexported"; +import { Default, NotDefault } from "./reexported"; export function usesDefaultExport(param: Default) {} + +export function usesNonDefaultExport(param: NotDefault) {} diff --git a/src/test/converter2/issues/gh2574/notDefault.ts b/src/test/converter2/issues/gh2574/notDefault.ts new file mode 100644 index 000000000..d2482e2c0 --- /dev/null +++ b/src/test/converter2/issues/gh2574/notDefault.ts @@ -0,0 +1,3 @@ +export class NotDefaultExport { + constructor() {} +} diff --git a/src/test/converter2/issues/gh2574/reexported.ts b/src/test/converter2/issues/gh2574/reexported.ts index 65fe0b80b..f09daf171 100644 --- a/src/test/converter2/issues/gh2574/reexported.ts +++ b/src/test/converter2/issues/gh2574/reexported.ts @@ -1 +1,2 @@ export { default as Default } from "./default"; +export { NotDefaultExport as NotDefault } from "./notDefault"; diff --git a/src/test/issues.c2.test.ts b/src/test/issues.c2.test.ts index 057b04767..ee25c0266 100644 --- a/src/test/issues.c2.test.ts +++ b/src/test/issues.c2.test.ts @@ -15,7 +15,6 @@ import { ProjectReflection, QueryType, ReferenceReflection, - ReferenceType, ReflectionKind, ReflectionType, SignatureReflection, @@ -92,21 +91,6 @@ describe("Issue Tests", () => { ); }); - it("#2574", () => { - const project = convert(); - const usesDefaultExport = query(project, "usesDefaultExport"); - const sig = usesDefaultExport.signatures?.[0]; - ok(sig, "Missing signature for usesDefaultExport"); - const param = sig.parameters?.[0]; - ok(param, "Missing parameter"); - equal(param.name, "param", "Incorrect parameter name"); - const paramType = param.type as ReferenceType | undefined; - ok(paramType, "Parameter type is not a reference type or undefined"); - equal(paramType.type, "reference", "Parameter is not a reference type"); - equal(paramType.name, "DefaultExport", "Incorrect reference name"); - equal(paramType.qualifiedName, "default", "Incorrect qualified name"); - }); - it("#671", () => { const project = convert(); const toNumber = query(project, "toNumber"); @@ -1460,4 +1444,40 @@ describe("Issue Tests", () => { app.validate(project); logger.expectNoOtherMessages(); }); + + it("#2574 default export", () => { + const project = convert(); + const sig = querySig(project, "usesDefaultExport"); + const param = sig.parameters?.[0]; + ok(param, "Missing parameter"); + equal(param.name, "param", "Incorrect parameter name"); + ok(param.type, "Parameter type is not a reference type or undefined"); + equal( + param.type!.type, + "reference", + "Parameter is not a reference type", + ); + equal(param.type!.name, "DefaultExport", "Incorrect reference name"); + equal(param.type!.qualifiedName, "default", "Incorrect qualified name"); + }); + + it("#2574 not default export", () => { + const project = convert(); + const sig = querySig(project, "usesNonDefaultExport"); + const param = sig.parameters?.[0]; + ok(param, "Missing parameter"); + equal(param.name, "param", "Incorrect parameter name"); + ok(param.type, "Parameter type is not a reference type or undefined"); + equal( + param.type!.type, + "reference", + "Parameter is not a reference type", + ); + equal(param.type!.name, "NotDefaultExport", "Incorrect reference name"); + equal( + param.type!.qualifiedName, + "NotDefaultExport", + "Incorrect qualified name", + ); + }); });