diff --git a/package.json b/package.json index 14bb6d39f82..a62f3c52cec 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "@actions/core": "^1.10.0", "@angular-devkit/architect-cli": "^0.1701.0-rc", "@angular/animations": "^17.1.0-next", - "@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#8a32f9fd9af99832f337db9fe29842797121e96d", + "@angular/build-tooling": "https://github.com/angular/dev-infra-private-build-tooling-builds.git#e97da496dc89481ef8e91433f2a1b674a2360340", "@angular/docs": "https://github.com/angular/dev-infra-private-docs-builds.git#1ff2479610f1f145433b95bbe85f6606e6e975c1", "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#cdbbb64c01e02d4edda03d66fa0919504d8c5dfc", "@babel/helper-remap-async-to-generator": "^7.18.9", diff --git a/packages/compiler-cli/src/ngtsc/docs/src/class_extractor.ts b/packages/compiler-cli/src/ngtsc/docs/src/class_extractor.ts index 7a51e08ecce..5e8b93fc44b 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/class_extractor.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/class_extractor.ts @@ -30,13 +30,20 @@ type PropertyDeclarationLike = ts.PropertyDeclaration|ts.AccessorDeclaration; /** Type representing either a class declaration ro an interface declaration. */ type ClassDeclarationLike = ts.ClassDeclaration|ts.InterfaceDeclaration; -/** Type representing either a class member node or an interface member node. */ +/** Type representing either a class or interface member. */ type MemberElement = ts.ClassElement|ts.TypeElement; -/** Type representing either a class method declaration or an interface method signature. */ +/** Type representing a signature element of an interface. */ +type SignatureElement = ts.CallSignatureDeclaration|ts.ConstructSignatureDeclaration; + +/** + * Type representing either: + */ type MethodLike = ts.MethodDeclaration|ts.MethodSignature; -/** Type representing either a class property declaration or an interface property signature. */ +/** + * Type representing either a class property declaration or an interface property signature. + */ type PropertyLike = PropertyDeclarationLike|ts.PropertySignature; /** Extractor to pull info for API reference documentation for a TypeScript class or interface. */ @@ -53,7 +60,7 @@ class ClassExtractor { isAbstract: this.isAbstract(), entryType: ts.isInterfaceDeclaration(this.declaration) ? EntryType.Interface : EntryType.UndecoratedClass, - members: this.extractAllClassMembers(), + members: this.extractSignatures().concat(this.extractAllClassMembers()), generics: extractGenerics(this.declaration), description: extractJsDocDescription(this.declaration), jsdocTags: extractJsDocTags(this.declaration), @@ -92,9 +99,15 @@ class ClassExtractor { return undefined; } + /** Extract docs for all call signatures in the current class/interface. */ + protected extractSignatures(): MemberEntry[] { + return this.computeAllSignatureDeclarations().map(s => this.extractSignature(s)); + } + /** Extracts docs for a class method. */ protected extractMethod(methodDeclaration: MethodLike): MethodEntry { - const functionExtractor = new FunctionExtractor(methodDeclaration, this.typeChecker); + const functionExtractor = new FunctionExtractor( + methodDeclaration.name.getText(), methodDeclaration, this.typeChecker); return { ...functionExtractor.extract(), memberType: MemberType.Method, @@ -102,6 +115,20 @@ class ClassExtractor { }; } + /** Extracts docs for a signature element (usually inside an interface). */ + protected extractSignature(signature: SignatureElement): MethodEntry { + // No name for the function if we are dealing with call signatures. + // For construct signatures we are using `new` as the name of the function for now. + // TODO: Consider exposing a new entry type for signature types. + const functionExtractor = new FunctionExtractor( + ts.isConstructSignatureDeclaration(signature) ? 'new' : '', signature, this.typeChecker); + return { + ...functionExtractor.extract(), + memberType: MemberType.Method, + memberTags: [], + }; + } + /** Extracts doc info for a property declaration. */ protected extractClassProperty(propertyDeclaration: PropertyLike): PropertyEntry { return { @@ -137,16 +164,35 @@ class ClassExtractor { return tags; } + /** Computes all signature declarations of the class/interface. */ + private computeAllSignatureDeclarations(): SignatureElement[] { + const type = this.typeChecker.getTypeAtLocation(this.declaration); + const signatures = [ + ...type.getCallSignatures(), + ...type.getConstructSignatures(), + ]; + + const result: SignatureElement[] = []; + for (const signature of signatures) { + const decl = signature.getDeclaration(); + if (this.isDocumentableSignature(decl) && this.isDocumentableMember(decl)) { + result.push(decl); + } + } + + return result; + } + /** Gets all member declarations, including inherited members. */ private getMemberDeclarations(): MemberElement[] { // We rely on TypeScript to resolve all the inherited members to their - // ultimate form via `getPropertiesOfType`. This is important because child + // ultimate form via `getProperties`. This is important because child // classes may narrow types or add method overloads. const type = this.typeChecker.getTypeAtLocation(this.declaration); const members = type.getProperties(); // While the properties of the declaration type represent the properties that exist - // on a clas *instance*, static members are properties on the class symbol itself. + // on a class *instance*, static members are properties on the class symbol itself. const typeOfConstructor = this.typeChecker.getTypeOfSymbol(type.symbol); const staticMembers = typeOfConstructor.getProperties(); @@ -221,6 +267,13 @@ class ClassExtractor { return ts.isMethodDeclaration(member) || ts.isMethodSignature(member); } + /** Gets whether the given signature declaration is documentable. */ + private isDocumentableSignature(signature: ts.SignatureDeclaration): + signature is SignatureElement { + return ts.isConstructSignatureDeclaration(signature) || + ts.isCallSignatureDeclaration(signature); + } + /** Gets whether the declaration for this extractor is abstract. */ private isAbstract(): boolean { const modifiers = this.declaration.modifiers ?? []; diff --git a/packages/compiler-cli/src/ngtsc/docs/src/entities.ts b/packages/compiler-cli/src/ngtsc/docs/src/entities.ts index 70952d2a2f7..059946ff86e 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/entities.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/entities.ts @@ -120,6 +120,7 @@ export interface FunctionEntry extends DocEntry { params: ParameterEntry[]; returnType: string; generics: GenericEntry[]; + isNewType: boolean; } /** Sub-entry for a single class or enum member. */ diff --git a/packages/compiler-cli/src/ngtsc/docs/src/extractor.ts b/packages/compiler-cli/src/ngtsc/docs/src/extractor.ts index 4888d342808..4b074ea6556 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/extractor.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/extractor.ts @@ -66,7 +66,8 @@ export class DocsExtractor { } if (ts.isFunctionDeclaration(node)) { - const functionExtractor = new FunctionExtractor(node, this.typeChecker); + // Name is guaranteed to be set, because it's exported directly. + const functionExtractor = new FunctionExtractor(node.name!.getText(), node, this.typeChecker); return functionExtractor.extract(); } @@ -107,7 +108,7 @@ export class DocsExtractor { for (let i = 0; i < declarationCount; i++) { const [exportName, declaration] = exportedDeclarations[i]; if (ts.isFunctionDeclaration(declaration)) { - const extractor = new FunctionExtractor(declaration, this.typeChecker); + const extractor = new FunctionExtractor(exportName, declaration, this.typeChecker); const overloads = extractor.getOverloads().map(overload => [exportName, overload] as const); exportedDeclarations.push(...overloads); diff --git a/packages/compiler-cli/src/ngtsc/docs/src/function_extractor.ts b/packages/compiler-cli/src/ngtsc/docs/src/function_extractor.ts index 818b12368d5..99e2867c493 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/function_extractor.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/function_extractor.ts @@ -13,10 +13,13 @@ import {extractGenerics} from './generics_extractor'; import {extractJsDocDescription, extractJsDocTags, extractRawJsDoc} from './jsdoc_extractor'; import {extractResolvedTypeString} from './type_extractor'; -export type FunctionLike = ts.FunctionDeclaration|ts.MethodDeclaration|ts.MethodSignature; +export type FunctionLike = ts.FunctionDeclaration|ts.MethodDeclaration|ts.MethodSignature| + ts.CallSignatureDeclaration|ts.ConstructSignatureDeclaration; export class FunctionExtractor { - constructor(private declaration: FunctionLike, private typeChecker: ts.TypeChecker) {} + constructor( + private name: string, private declaration: FunctionLike, + private typeChecker: ts.TypeChecker) {} extract(): FunctionEntry { // TODO: is there any real situation in which the signature would not be available here? @@ -28,9 +31,8 @@ export class FunctionExtractor { return { params: this.extractAllParams(this.declaration.parameters), - // We know that the function has a name here because we would have skipped it - // already before getting to this point if it was anonymous. - name: this.declaration.name!.getText(), + name: this.name, + isNewType: ts.isConstructSignatureDeclaration(this.declaration), returnType, entryType: EntryType.Function, generics: extractGenerics(this.declaration), diff --git a/yarn.lock b/yarn.lock index 1052991a426..9d6e8599bcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -298,10 +298,9 @@ "@angular/core" "^13.0.0 || ^14.0.0-0" reflect-metadata "^0.1.13" -"@angular/build-tooling@https://github.com/angular/dev-infra-private-build-tooling-builds.git#8a32f9fd9af99832f337db9fe29842797121e96d": - version "0.0.0-ae9155d1083fd8390e98bfe09a5ccf89f0375b52" - uid "8a32f9fd9af99832f337db9fe29842797121e96d" - resolved "https://github.com/angular/dev-infra-private-build-tooling-builds.git#8a32f9fd9af99832f337db9fe29842797121e96d" +"@angular/build-tooling@https://github.com/angular/dev-infra-private-build-tooling-builds.git#e97da496dc89481ef8e91433f2a1b674a2360340": + version "0.0.0-f0fa701114b57b7d633a2ab3813034ab1735e3c0" + resolved "https://github.com/angular/dev-infra-private-build-tooling-builds.git#e97da496dc89481ef8e91433f2a1b674a2360340" dependencies: "@angular-devkit/build-angular" "17.1.0-rc.1" "@angular/benchpress" "0.3.0" @@ -332,7 +331,7 @@ marked-mangle "^1.1.4" preact "^10.17.1" preact-render-to-string "^6.2.1" - prettier "3.2.2" + prettier "3.2.4" protractor "^7.0.0" selenium-webdriver "4.16.0" send "^0.18.0" @@ -400,7 +399,6 @@ "@angular/docs@https://github.com/angular/dev-infra-private-docs-builds.git#1ff2479610f1f145433b95bbe85f6606e6e975c1": version "0.0.0-ae9155d1083fd8390e98bfe09a5ccf89f0375b52" - uid "1ff2479610f1f145433b95bbe85f6606e6e975c1" resolved "https://github.com/angular/dev-infra-private-docs-builds.git#1ff2479610f1f145433b95bbe85f6606e6e975c1" dependencies: "@angular/cdk" "17.1.0-rc.0" @@ -488,7 +486,6 @@ "@angular/ng-dev@https://github.com/angular/dev-infra-private-ng-dev-builds.git#cdbbb64c01e02d4edda03d66fa0919504d8c5dfc": version "0.0.0-ae9155d1083fd8390e98bfe09a5ccf89f0375b52" - uid cdbbb64c01e02d4edda03d66fa0919504d8c5dfc resolved "https://github.com/angular/dev-infra-private-ng-dev-builds.git#cdbbb64c01e02d4edda03d66fa0919504d8c5dfc" dependencies: "@yarnpkg/lockfile" "^1.1.0" @@ -13448,10 +13445,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prettier@3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.2.tgz#96e580f7ca9c96090ad054616c0c4597e2844b65" - integrity sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A== +prettier@3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.4.tgz#4723cadeac2ce7c9227de758e5ff9b14e075f283" + integrity sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ== prettier@^3.0.0: version "3.0.3" @@ -14589,6 +14586,7 @@ select-hose@^2.0.0: integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== "selenium-webdriver4@npm:selenium-webdriver@4.16.0", selenium-webdriver@4.16.0: + name selenium-webdriver4 version "4.16.0" resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-4.16.0.tgz#2f1a2426d876aa389d1c937b00f034c2c7808360" integrity sha512-IbqpRpfGE7JDGgXHJeWuCqT/tUqnLvZ14csSwt+S8o4nJo3RtQoE9VR4jB47tP/A8ArkYsh/THuMY6kyRP6kuA==