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 68decde1e83..7038b540245 100644 --- a/packages/compiler-cli/src/ngtsc/docs/src/function_extractor.ts +++ b/packages/compiler-cli/src/ngtsc/docs/src/function_extractor.ts @@ -42,8 +42,18 @@ export class FunctionExtractor { const overloads = ts.isConstructorDeclaration(this.exportDeclaration) ? constructorOverloads(this.exportDeclaration, this.typeChecker) : extractCallSignatures(this.name, this.typeChecker, type); - const jsdocsTags = extractJsDocTags(implementation); - const description = extractJsDocDescription(implementation); + const implementationJsDocTags = extractJsDocTags(implementation); + const implementationDescription = extractJsDocDescription(implementation); + const implementationRawComment = extractRawJsDoc(implementation); + const docsForFunctionEntry = extractFunctionEntryDocs(overloads, { + description: implementationDescription, + jsdocTags: implementationJsDocTags, + rawComment: implementationRawComment, + }) ?? { + description: implementationDescription, + jsdocTags: implementationJsDocTags, + rawComment: implementationRawComment, + }; return { name: this.name, @@ -52,22 +62,41 @@ export class FunctionExtractor { params: extractAllParams(implementation.parameters, this.typeChecker), isNewType: ts.isConstructSignatureDeclaration(implementation), returnType, - returnDescription: jsdocsTags.find((tag) => tag.name === 'returns')?.comment, + returnDescription: implementationJsDocTags.find((tag) => tag.name === 'returns')?.comment, generics: extractGenerics(implementation), name: this.name, - description, + description: implementationDescription, entryType: EntryType.Function, - jsdocTags: jsdocsTags, - rawComment: extractRawJsDoc(implementation), + jsdocTags: implementationJsDocTags, + rawComment: implementationRawComment, }, entryType: EntryType.Function, - description, - jsdocTags: jsdocsTags, - rawComment: extractRawJsDoc(implementation), + description: docsForFunctionEntry.description, + jsdocTags: docsForFunctionEntry.jsdocTags, + rawComment: docsForFunctionEntry.rawComment, }; } } +function extractFunctionEntryDocs( + overloads: FunctionSignatureMetadata[], + implementationDocs: Pick, +): Pick | null { + if (hasJSDocContent(implementationDocs)) { + return implementationDocs; + } + + return overloads.find((overload) => hasJSDocContent(overload)) ?? null; +} + +function hasJSDocContent( + docs: Pick, +): boolean { + return ( + docs.description.trim() !== '' || docs.rawComment.trim() !== '' || docs.jsdocTags.length > 0 + ); +} + function constructorOverloads( constructorDeclaration: ts.ConstructorDeclaration, typeChecker: ts.TypeChecker, diff --git a/packages/compiler-cli/test/ngtsc/doc_extraction/function_doc_extraction_spec.ts b/packages/compiler-cli/test/ngtsc/doc_extraction/function_doc_extraction_spec.ts index 63f81085036..99b7e6ebb7a 100644 --- a/packages/compiler-cli/test/ngtsc/doc_extraction/function_doc_extraction_spec.ts +++ b/packages/compiler-cli/test/ngtsc/doc_extraction/function_doc_extraction_spec.ts @@ -126,6 +126,56 @@ runInEachFileSystem(() => { expect(numberOverloadEntry.returnType).toBe('number'); }); + it('should use overload docs when implementation has no docs', () => { + env.write( + 'index.ts', + ` + /** + * Overload docs. + * + * @publicApi + */ + export function ident(value: boolean): boolean + export function ident(value: boolean|number): boolean|number { + return value; + } + `, + ); + + const docs = env.driveDocsExtraction('index.ts') as FunctionEntry[]; + const [functionEntry] = docs; + + expect(functionEntry.description).toContain('Overload docs.'); + expect(functionEntry.rawComment).toContain('Overload docs.'); + expect(functionEntry.jsdocTags.some((tag) => tag.name === 'publicApi')).toBeTrue(); + expect(functionEntry.implementation.description).toBe(''); + }); + + it('should prefer implementation docs over overload docs', () => { + env.write( + 'index.ts', + ` + /** Overload docs. */ + export function ident(value: boolean): boolean + /** + * Implementation docs. + * + * @publicApi + */ + export function ident(value: boolean|number): boolean|number { + return value; + } + `, + ); + + const docs = env.driveDocsExtraction('index.ts') as FunctionEntry[]; + const [functionEntry] = docs; + + expect(functionEntry.description).toContain('Implementation docs.'); + expect(functionEntry.rawComment).toContain('Implementation docs.'); + expect(functionEntry.jsdocTags.some((tag) => tag.name === 'publicApi')).toBeTrue(); + }); + it('should extract function generics', () => { env.write( 'index.ts',