fix(api-extractor): links including entrypoints (#10924)

This commit is contained in:
Qjuh
2025-06-07 13:17:29 +02:00
committed by GitHub
parent db8c1d3edb
commit 9708717204
3 changed files with 100 additions and 57 deletions

View File

@@ -56,6 +56,7 @@ import type { ApiItemMetadata } from '../collector/ApiItemMetadata.js';
import type { Collector } from '../collector/Collector.js'; import type { Collector } from '../collector/Collector.js';
import type { DeclarationMetadata } from '../collector/DeclarationMetadata.js'; import type { DeclarationMetadata } from '../collector/DeclarationMetadata.js';
import type { ISourceLocation } from '../collector/SourceMapper.js'; import type { ISourceLocation } from '../collector/SourceMapper.js';
import type { IWorkingPackageEntryPoint } from '../collector/WorkingPackage.js';
import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator.js'; import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator.js';
import { ExcerptBuilder, type IExcerptBuilderNodeToCapture } from './ExcerptBuilder.js'; import { ExcerptBuilder, type IExcerptBuilderNodeToCapture } from './ExcerptBuilder.js';
@@ -205,6 +206,7 @@ export interface DocgenJson {
typedefs: DocgenTypedefJson[]; typedefs: DocgenTypedefJson[];
} }
interface IProcessAstEntityContext { interface IProcessAstEntityContext {
entryPoint: IWorkingPackageEntryPoint;
isExported: boolean; isExported: boolean;
name: string; name: string;
parentApiItem: ApiItemContainerMixin; parentApiItem: ApiItemContainerMixin;
@@ -305,6 +307,7 @@ export class ApiModelGenerator {
// we are including forgotten exports, then process everything. // we are including forgotten exports, then process everything.
if (entity.exportedFromEntryPoint || this._collector.extractorConfig.docModelIncludeForgottenExports) { if (entity.exportedFromEntryPoint || this._collector.extractorConfig.docModelIncludeForgottenExports) {
this._processAstEntity(entity.astEntity, { this._processAstEntity(entity.astEntity, {
entryPoint,
name: entity.nameForEmit!, name: entity.nameForEmit!,
isExported: entity.exportedFromEntryPoint, isExported: entity.exportedFromEntryPoint,
parentApiItem: apiEntryPoint, parentApiItem: apiEntryPoint,
@@ -352,7 +355,7 @@ export class ApiModelGenerator {
private _processAstNamespaceImport(astNamespaceImport: AstNamespaceImport, context: IProcessAstEntityContext): void { private _processAstNamespaceImport(astNamespaceImport: AstNamespaceImport, context: IProcessAstEntityContext): void {
const astModule: AstModule = astNamespaceImport.astModule; const astModule: AstModule = astNamespaceImport.astModule;
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiNamespace.getContainerKey(name); const containerKey: string = ApiNamespace.getContainerKey(name);
const sourceLocation: ISourceLocation = this._getSourceLocation(astNamespaceImport.declaration); const sourceLocation: ISourceLocation = this._getSourceLocation(astNamespaceImport.declaration);
@@ -375,6 +378,7 @@ export class ApiModelGenerator {
// eslint-disable-next-line unicorn/no-array-for-each // eslint-disable-next-line unicorn/no-array-for-each
astModule.astModuleExportInfo!.exportedLocalEntities.forEach((exportedEntity: AstEntity, exportedName: string) => { astModule.astModuleExportInfo!.exportedLocalEntities.forEach((exportedEntity: AstEntity, exportedName: string) => {
this._processAstEntity(exportedEntity, { this._processAstEntity(exportedEntity, {
entryPoint,
name: exportedName, name: exportedName,
isExported: true, isExported: true,
parentApiItem: apiNamespace!, parentApiItem: apiNamespace!,
@@ -533,7 +537,7 @@ export class ApiModelGenerator {
} }
private _processApiCallSignature(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiCallSignature(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { parentApiItem } = context; const { entryPoint, parentApiItem } = context;
const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration);
const containerKey: string = ApiCallSignature.getContainerKey(overloadIndex); const containerKey: string = ApiCallSignature.getContainerKey(overloadIndex);
@@ -556,7 +560,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, callSignature.parameters); const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, callSignature.parameters);
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
@@ -580,7 +584,7 @@ export class ApiModelGenerator {
} }
private _processApiConstructor(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiConstructor(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { parentApiItem } = context; const { entryPoint, parentApiItem } = context;
const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration);
const containerKey: string = ApiConstructor.getContainerKey(overloadIndex); const containerKey: string = ApiConstructor.getContainerKey(overloadIndex);
@@ -597,7 +601,7 @@ export class ApiModelGenerator {
constructorDeclaration.parameters, constructorDeclaration.parameters,
); );
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = parent?.construct const docComment: tsdoc.DocComment | undefined = parent?.construct
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -629,7 +633,7 @@ export class ApiModelGenerator {
} }
private _processApiClass(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiClass(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiClass.getContainerKey(name); const containerKey: string = ApiClass.getContainerKey(name);
let apiClass: ApiClass | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiClass; let apiClass: ApiClass | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiClass;
@@ -683,7 +687,7 @@ export class ApiModelGenerator {
} }
} }
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -732,7 +736,7 @@ export class ApiModelGenerator {
} }
private _processApiConstructSignature(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiConstructSignature(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { parentApiItem } = context; const { entryPoint, parentApiItem } = context;
const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration);
const containerKey: string = ApiConstructSignature.getContainerKey(overloadIndex); const containerKey: string = ApiConstructSignature.getContainerKey(overloadIndex);
@@ -757,7 +761,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, constructSignature.parameters); const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, constructSignature.parameters);
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = parent?.construct const docComment: tsdoc.DocComment | undefined = parent?.construct
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -789,13 +793,13 @@ export class ApiModelGenerator {
} }
private _processApiEnum(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiEnum(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiEnum.getContainerKey(name); const containerKey: string = ApiEnum.getContainerKey(name);
let apiEnum: ApiEnum | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiEnum; let apiEnum: ApiEnum | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiEnum;
if (apiEnum === undefined) { if (apiEnum === undefined) {
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, []); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, [], entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
@@ -823,7 +827,7 @@ export class ApiModelGenerator {
} }
private _processApiEnumMember(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiEnumMember(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, parentApiItem } = context; const { entryPoint, name, parentApiItem } = context;
const containerKey: string = ApiEnumMember.getContainerKey(name); const containerKey: string = ApiEnumMember.getContainerKey(name);
let apiEnumMember: ApiEnumMember | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiEnumMember; let apiEnumMember: ApiEnumMember | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiEnumMember;
@@ -839,7 +843,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: enumMember.initializer, tokenRange: initializerTokenRange }); nodesToCapture.push({ node: enumMember.initializer, tokenRange: initializerTokenRange });
} }
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
@@ -865,7 +869,7 @@ export class ApiModelGenerator {
context: IProcessAstEntityContext, context: IProcessAstEntityContext,
altFunctionDeclaration?: ts.FunctionDeclaration, altFunctionDeclaration?: ts.FunctionDeclaration,
): void { ): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration);
const containerKey: string = ApiFunction.getContainerKey(name, overloadIndex); const containerKey: string = ApiFunction.getContainerKey(name, overloadIndex);
@@ -894,7 +898,7 @@ export class ApiModelGenerator {
jsDoc?.params, jsDoc?.params,
); );
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -938,7 +942,7 @@ export class ApiModelGenerator {
} }
private _processApiIndexSignature(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiIndexSignature(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { parentApiItem } = context; const { entryPoint, parentApiItem } = context;
const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration); const overloadIndex: number = this._collector.getOverloadIndex(astDeclaration);
const containerKey: string = ApiIndexSignature.getContainerKey(overloadIndex); const containerKey: string = ApiIndexSignature.getContainerKey(overloadIndex);
@@ -956,7 +960,7 @@ export class ApiModelGenerator {
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, indexSignature.parameters); const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, indexSignature.parameters);
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
@@ -981,7 +985,7 @@ export class ApiModelGenerator {
} }
private _processApiInterface(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiInterface(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiInterface.getContainerKey(name); const containerKey: string = ApiInterface.getContainerKey(name);
let apiInterface: ApiInterface | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiInterface; let apiInterface: ApiInterface | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiInterface;
@@ -1022,7 +1026,7 @@ export class ApiModelGenerator {
} }
} }
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -1064,7 +1068,7 @@ export class ApiModelGenerator {
} }
private _processApiMethod(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void { private _processApiMethod(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void {
const { name, parentApiItem } = context; const { entryPoint, name, parentApiItem } = context;
const parent = context.parentDocgenJson as DocgenClassJson | DocgenInterfaceJson | undefined; const parent = context.parentDocgenJson as DocgenClassJson | DocgenInterfaceJson | undefined;
const jsDoc = parent?.methods?.find((method) => method.name === name); const jsDoc = parent?.methods?.find((method) => method.name === name);
const isStatic: boolean = astDeclaration const isStatic: boolean = astDeclaration
@@ -1095,7 +1099,7 @@ export class ApiModelGenerator {
jsDoc?.params, jsDoc?.params,
); );
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -1163,7 +1167,7 @@ export class ApiModelGenerator {
} }
private _processApiMethodSignature(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void { private _processApiMethodSignature(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void {
const { name, parentApiItem } = context; const { entryPoint, name, parentApiItem } = context;
const overloadIndex: number = astDeclaration ? this._collector.getOverloadIndex(astDeclaration) : 1; const overloadIndex: number = astDeclaration ? this._collector.getOverloadIndex(astDeclaration) : 1;
const containerKey: string = ApiMethodSignature.getContainerKey(name, overloadIndex); const containerKey: string = ApiMethodSignature.getContainerKey(name, overloadIndex);
@@ -1193,7 +1197,7 @@ export class ApiModelGenerator {
jsDoc?.params, jsDoc?.params,
); );
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -1241,13 +1245,13 @@ export class ApiModelGenerator {
} }
private _processApiNamespace(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiNamespace(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiNamespace.getContainerKey(name); const containerKey: string = ApiNamespace.getContainerKey(name);
let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiNamespace; let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiNamespace;
if (apiNamespace === undefined) { if (apiNamespace === undefined) {
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, []); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, [], entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
@@ -1273,7 +1277,7 @@ export class ApiModelGenerator {
} }
private _processApiProperty(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void { private _processApiProperty(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void {
const { name, parentApiItem } = context; const { entryPoint, name, parentApiItem } = context;
const parent = context.parentDocgenJson as DocgenClassJson | DocgenInterfaceJson | DocgenTypedefJson | undefined; const parent = context.parentDocgenJson as DocgenClassJson | DocgenInterfaceJson | DocgenTypedefJson | undefined;
const jsDoc = parent?.props?.find((prop) => prop.name === name); const jsDoc = parent?.props?.find((prop) => prop.name === name);
const isStatic: boolean = astDeclaration const isStatic: boolean = astDeclaration
@@ -1314,7 +1318,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: declaration.initializer, tokenRange: initializerTokenRange }); nodesToCapture.push({ node: declaration.initializer, tokenRange: initializerTokenRange });
} }
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -1371,7 +1375,7 @@ export class ApiModelGenerator {
} }
private _processApiPropertySignature(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void { private _processApiPropertySignature(astDeclaration: AstDeclaration | null, context: IProcessAstEntityContext): void {
const { name, parentApiItem } = context; const { entryPoint, name, parentApiItem } = context;
const containerKey: string = ApiPropertySignature.getContainerKey(name); const containerKey: string = ApiPropertySignature.getContainerKey(name);
let apiPropertySignature: ApiPropertySignature | undefined = parentApiItem.tryGetMemberByKey( let apiPropertySignature: ApiPropertySignature | undefined = parentApiItem.tryGetMemberByKey(
@@ -1392,7 +1396,7 @@ export class ApiModelGenerator {
const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
nodesToCapture.push({ node: propertySignature.type, tokenRange: propertyTypeTokenRange }); nodesToCapture.push({ node: propertySignature.type, tokenRange: propertyTypeTokenRange });
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -1440,7 +1444,7 @@ export class ApiModelGenerator {
} }
private _processApiTypeAlias(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiTypeAlias(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiTypeAlias.getContainerKey(name); const containerKey: string = ApiTypeAlias.getContainerKey(name);
@@ -1464,7 +1468,7 @@ export class ApiModelGenerator {
const typeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange(); const typeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
nodesToCapture.push({ node: typeAliasDeclaration.type, tokenRange: typeTokenRange }); nodesToCapture.push({ node: typeAliasDeclaration.type, tokenRange: typeTokenRange });
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = jsDoc const docComment: tsdoc.DocComment | undefined = jsDoc
? this._tsDocParser.parseString( ? this._tsDocParser.parseString(
@@ -1506,7 +1510,7 @@ export class ApiModelGenerator {
} }
private _processApiVariable(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void { private _processApiVariable(astDeclaration: AstDeclaration, context: IProcessAstEntityContext): void {
const { name, isExported, parentApiItem } = context; const { entryPoint, name, isExported, parentApiItem } = context;
const containerKey: string = ApiVariable.getContainerKey(name); const containerKey: string = ApiVariable.getContainerKey(name);
@@ -1526,7 +1530,7 @@ export class ApiModelGenerator {
nodesToCapture.push({ node: variableDeclaration.initializer, tokenRange: initializerTokenRange }); nodesToCapture.push({ node: variableDeclaration.initializer, tokenRange: initializerTokenRange });
} }
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture); const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture, entryPoint);
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration); const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment; const docComment: tsdoc.DocComment | undefined = apiItemMetadata.tsdocComment;
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag; const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
@@ -1652,18 +1656,25 @@ export class ApiModelGenerator {
private _buildExcerptTokens( private _buildExcerptTokens(
astDeclaration: AstDeclaration, astDeclaration: AstDeclaration,
nodesToCapture: IExcerptBuilderNodeToCapture[], nodesToCapture: IExcerptBuilderNodeToCapture[],
entryPoint: IWorkingPackageEntryPoint,
): IExcerptToken[] { ): IExcerptToken[] {
const excerptTokens: IExcerptToken[] = []; const excerptTokens: IExcerptToken[] = [];
// Build the main declaration // Build the main declaration
ExcerptBuilder.addDeclaration(excerptTokens, astDeclaration, nodesToCapture, this._referenceGenerator); ExcerptBuilder.addDeclaration(excerptTokens, astDeclaration, nodesToCapture, this._referenceGenerator, entryPoint);
const declarationMetadata: DeclarationMetadata = this._collector.fetchDeclarationMetadata(astDeclaration); const declarationMetadata: DeclarationMetadata = this._collector.fetchDeclarationMetadata(astDeclaration);
// Add any ancillary declarations // Add any ancillary declarations
for (const ancillaryDeclaration of declarationMetadata.ancillaryDeclarations) { for (const ancillaryDeclaration of declarationMetadata.ancillaryDeclarations) {
ExcerptBuilder.addBlankLine(excerptTokens); ExcerptBuilder.addBlankLine(excerptTokens);
ExcerptBuilder.addDeclaration(excerptTokens, ancillaryDeclaration, nodesToCapture, this._referenceGenerator); ExcerptBuilder.addDeclaration(
excerptTokens,
ancillaryDeclaration,
nodesToCapture,
this._referenceGenerator,
entryPoint,
);
} }
return excerptTokens; return excerptTokens;

View File

@@ -15,6 +15,7 @@ import { TypeScriptHelpers } from '../analyzer/TypeScriptHelpers.js';
import { TypeScriptInternals } from '../analyzer/TypeScriptInternals.js'; import { TypeScriptInternals } from '../analyzer/TypeScriptInternals.js';
import type { Collector } from '../collector/Collector.js'; import type { Collector } from '../collector/Collector.js';
import type { CollectorEntity } from '../collector/CollectorEntity.js'; import type { CollectorEntity } from '../collector/CollectorEntity.js';
import type { IWorkingPackageEntryPoint } from '../collector/WorkingPackage.js';
export class DeclarationReferenceGenerator { export class DeclarationReferenceGenerator {
public static readonly unknownReference: string = '?'; public static readonly unknownReference: string = '?';
@@ -28,14 +29,25 @@ export class DeclarationReferenceGenerator {
/** /**
* Gets the UID for a TypeScript Identifier that references a type. * Gets the UID for a TypeScript Identifier that references a type.
*/ */
public getDeclarationReferenceForIdentifier(node: ts.Identifier): DeclarationReference | undefined { public getDeclarationReferenceForIdentifier(
node: ts.Identifier,
entryPoint: IWorkingPackageEntryPoint,
): DeclarationReference | undefined {
const symbol: ts.Symbol | undefined = this._collector.typeChecker.getSymbolAtLocation(node); const symbol: ts.Symbol | undefined = this._collector.typeChecker.getSymbolAtLocation(node);
if (symbol !== undefined) { if (symbol !== undefined) {
const isExpression: boolean = DeclarationReferenceGenerator._isInExpressionContext(node); const isExpression: boolean = DeclarationReferenceGenerator._isInExpressionContext(node);
return ( return (
this.getDeclarationReferenceForSymbol(symbol, isExpression ? ts.SymbolFlags.Value : ts.SymbolFlags.Type) ?? this.getDeclarationReferenceForSymbol(
this.getDeclarationReferenceForSymbol(symbol, isExpression ? ts.SymbolFlags.Type : ts.SymbolFlags.Value) ?? symbol,
this.getDeclarationReferenceForSymbol(symbol, ts.SymbolFlags.Namespace) isExpression ? ts.SymbolFlags.Value : ts.SymbolFlags.Type,
entryPoint,
) ??
this.getDeclarationReferenceForSymbol(
symbol,
isExpression ? ts.SymbolFlags.Type : ts.SymbolFlags.Value,
entryPoint,
) ??
this.getDeclarationReferenceForSymbol(symbol, ts.SymbolFlags.Namespace, entryPoint)
); );
} }
@@ -48,8 +60,9 @@ export class DeclarationReferenceGenerator {
public getDeclarationReferenceForSymbol( public getDeclarationReferenceForSymbol(
symbol: ts.Symbol, symbol: ts.Symbol,
meaning: ts.SymbolFlags, meaning: ts.SymbolFlags,
entryPoint: IWorkingPackageEntryPoint,
): DeclarationReference | undefined { ): DeclarationReference | undefined {
return this._symbolToDeclarationReference(symbol, meaning, /* includeModuleSymbols*/ false); return this._symbolToDeclarationReference(symbol, meaning, /* includeModuleSymbols*/ false, entryPoint);
} }
private static _isInExpressionContext(node: ts.Node): boolean { private static _isInExpressionContext(node: ts.Node): boolean {
@@ -197,6 +210,7 @@ export class DeclarationReferenceGenerator {
symbol: ts.Symbol, symbol: ts.Symbol,
meaning: ts.SymbolFlags, meaning: ts.SymbolFlags,
includeModuleSymbols: boolean, includeModuleSymbols: boolean,
entryPoint: IWorkingPackageEntryPoint,
): DeclarationReference | undefined { ): DeclarationReference | undefined {
const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol);
const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile();
@@ -221,7 +235,7 @@ export class DeclarationReferenceGenerator {
return undefined; return undefined;
} }
return new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); return new DeclarationReference(this._sourceFileToModuleSource(sourceFile, entryPoint));
} }
// Do not generate a declaration reference for a type parameter. // Do not generate a declaration reference for a type parameter.
@@ -229,7 +243,7 @@ export class DeclarationReferenceGenerator {
return undefined; return undefined;
} }
let parentRef: DeclarationReference | undefined = this._getParentReference(followedSymbol); let parentRef: DeclarationReference | undefined = this._getParentReference(followedSymbol, entryPoint);
if (!parentRef) { if (!parentRef) {
return undefined; return undefined;
} }
@@ -276,7 +290,10 @@ export class DeclarationReferenceGenerator {
.withMeaning(DeclarationReferenceGenerator._getMeaningOfSymbol(followedSymbol, meaning) as any); .withMeaning(DeclarationReferenceGenerator._getMeaningOfSymbol(followedSymbol, meaning) as any);
} }
private _getParentReference(symbol: ts.Symbol): DeclarationReference | undefined { private _getParentReference(
symbol: ts.Symbol,
entryPoint: IWorkingPackageEntryPoint,
): DeclarationReference | undefined {
const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol); const declaration: ts.Node | undefined = TypeScriptHelpers.tryGetADeclaration(symbol);
const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile(); const sourceFile: ts.SourceFile | undefined = declaration?.getSourceFile();
@@ -286,7 +303,7 @@ export class DeclarationReferenceGenerator {
const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(symbol); const entity: CollectorEntity | undefined = this._collector.tryGetEntityForSymbol(symbol);
if (entity) { if (entity) {
if (entity.exportedFromEntryPoint) { if (entity.exportedFromEntryPoint) {
return new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); return new DeclarationReference(this._sourceFileToModuleSource(sourceFile, entryPoint));
} }
const firstExportingConsumableParent: CollectorEntity | undefined = entity.getFirstExportingConsumableParent(); const firstExportingConsumableParent: CollectorEntity | undefined = entity.getFirstExportingConsumableParent();
@@ -296,7 +313,12 @@ export class DeclarationReferenceGenerator {
this._collector.typeChecker, this._collector.typeChecker,
); );
if (parentSymbol) { if (parentSymbol) {
return this._symbolToDeclarationReference(parentSymbol, parentSymbol.flags, /* includeModuleSymbols*/ true); return this._symbolToDeclarationReference(
parentSymbol,
parentSymbol.flags,
/* includeModuleSymbols*/ true,
entryPoint,
);
} }
} }
} }
@@ -304,7 +326,12 @@ export class DeclarationReferenceGenerator {
// Next, try to find a parent symbol via the symbol tree. // Next, try to find a parent symbol via the symbol tree.
const parentSymbol: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol); const parentSymbol: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol);
if (parentSymbol) { if (parentSymbol) {
return this._symbolToDeclarationReference(parentSymbol, parentSymbol.flags, /* includeModuleSymbols*/ true); return this._symbolToDeclarationReference(
parentSymbol,
parentSymbol.flags,
/* includeModuleSymbols*/ true,
entryPoint,
);
} }
// If that doesn't work, try to find a parent symbol via the node tree. As far as we can tell, // If that doesn't work, try to find a parent symbol via the node tree. As far as we can tell,
@@ -330,20 +357,22 @@ export class DeclarationReferenceGenerator {
grandParentSymbol, grandParentSymbol,
grandParentSymbol.flags, grandParentSymbol.flags,
/* includeModuleSymbols*/ true, /* includeModuleSymbols*/ true,
entryPoint,
); );
} }
} }
// At this point, we have a local symbol in a module. // At this point, we have a local symbol in a module.
if (sourceFile && ts.isExternalModule(sourceFile)) { if (sourceFile && ts.isExternalModule(sourceFile)) {
return new DeclarationReference(this._sourceFileToModuleSource(sourceFile)); return new DeclarationReference(this._sourceFileToModuleSource(sourceFile, entryPoint));
} else { } else {
return new DeclarationReference(GlobalSource.instance); return new DeclarationReference(GlobalSource.instance);
} }
} }
private _getEntryPointName(sourceFile: ts.SourceFile): string { private _getEntryPointName(sourceFile: ts.SourceFile, entry: IWorkingPackageEntryPoint): string {
if (this._collector.program.isSourceFileFromExternalLibrary(sourceFile)) { if (this._collector.program.isSourceFileFromExternalLibrary(sourceFile)) {
console.log(sourceFile.fileName, 'is external!');
const packageJson: INodePackageJson | undefined = this._collector.packageJsonLookup.tryLoadNodePackageJsonFor( const packageJson: INodePackageJson | undefined = this._collector.packageJsonLookup.tryLoadNodePackageJsonFor(
sourceFile.fileName, sourceFile.fileName,
); );
@@ -365,19 +394,17 @@ export class DeclarationReferenceGenerator {
return DeclarationReferenceGenerator.unknownReference; return DeclarationReferenceGenerator.unknownReference;
} }
let modulePath = ''; const modulePath = entry.modulePath;
for (const entryPoint of this._collector.workingPackage.entryPoints) {
if (entryPoint.sourceFile === sourceFile) {
modulePath = entryPoint.modulePath;
}
}
return `${this._collector.workingPackage.name}${modulePath ? `/${modulePath}` : ''}`; return `${this._collector.workingPackage.name}${modulePath ? `/${modulePath}` : ''}`;
} }
private _sourceFileToModuleSource(sourceFile: ts.SourceFile | undefined): GlobalSource | ModuleSource { private _sourceFileToModuleSource(
sourceFile: ts.SourceFile | undefined,
entryPoint: IWorkingPackageEntryPoint,
): GlobalSource | ModuleSource {
if (sourceFile && ts.isExternalModule(sourceFile)) { if (sourceFile && ts.isExternalModule(sourceFile)) {
const packageName: string = this._getEntryPointName(sourceFile); const packageName: string = this._getEntryPointName(sourceFile, entryPoint);
if (this._collector.bundledPackageNames.has(packageName)) { if (this._collector.bundledPackageNames.has(packageName)) {
// The api-extractor.json config file has a "bundledPackages" setting, which causes imports from // The api-extractor.json config file has a "bundledPackages" setting, which causes imports from

View File

@@ -11,6 +11,7 @@ import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/De
import * as ts from 'typescript'; import * as ts from 'typescript';
import type { AstDeclaration } from '../analyzer/AstDeclaration.js'; import type { AstDeclaration } from '../analyzer/AstDeclaration.js';
import { Span } from '../analyzer/Span.js'; import { Span } from '../analyzer/Span.js';
import type { IWorkingPackageEntryPoint } from '../collector/WorkingPackage.js';
import type { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator.js'; import type { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator.js';
/** /**
@@ -32,6 +33,8 @@ export interface IExcerptBuilderNodeToCapture {
* Internal state for ExcerptBuilder * Internal state for ExcerptBuilder
*/ */
interface IBuildSpanState { interface IBuildSpanState {
entryPoint: IWorkingPackageEntryPoint;
/** /**
* Tracks whether the last appended token was a separator. If so, and we're in the middle of * Tracks whether the last appended token was a separator. If so, and we're in the middle of
* capturing a token range, then omit the separator from the range. * capturing a token range, then omit the separator from the range.
@@ -90,6 +93,7 @@ export class ExcerptBuilder {
astDeclaration: AstDeclaration, astDeclaration: AstDeclaration,
nodesToCapture: IExcerptBuilderNodeToCapture[], nodesToCapture: IExcerptBuilderNodeToCapture[],
referenceGenerator: DeclarationReferenceGenerator, referenceGenerator: DeclarationReferenceGenerator,
entryPoint: IWorkingPackageEntryPoint,
): void { ): void {
let stopBeforeChildKind: ts.SyntaxKind | undefined; let stopBeforeChildKind: ts.SyntaxKind | undefined;
@@ -118,6 +122,7 @@ export class ExcerptBuilder {
} }
ExcerptBuilder._buildSpan(excerptTokens, span, { ExcerptBuilder._buildSpan(excerptTokens, span, {
entryPoint,
referenceGenerator, referenceGenerator,
startingNode: span.node, startingNode: span.node,
stopBeforeChildKind, stopBeforeChildKind,
@@ -185,7 +190,7 @@ export class ExcerptBuilder {
if (ts.isIdentifier(span.node)) { if (ts.isIdentifier(span.node)) {
const name: ts.Identifier = span.node; const name: ts.Identifier = span.node;
canonicalReference = state.referenceGenerator.getDeclarationReferenceForIdentifier(name); canonicalReference = state.referenceGenerator.getDeclarationReferenceForIdentifier(name, state.entryPoint);
} }
if (canonicalReference) { if (canonicalReference) {