Skip to content

Commit e616456

Browse files
committed
Update ExcerptBuilder to emit ancillary declarations
1 parent dea9b87 commit e616456

File tree

4 files changed

+103
-114
lines changed

4 files changed

+103
-114
lines changed

apps/api-extractor-model/src/items/ApiDeclaredItem.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@ export class ApiDeclaredItem extends ApiDocumentedItem {
3939
public constructor(options: IApiDeclaredItemOptions) {
4040
super(options);
4141

42-
this._excerptTokens = options.excerptTokens.map(x => {
43-
const canonicalReference: DeclarationReference | undefined = x.canonicalReference === undefined ? undefined :
44-
DeclarationReference.parse(x.canonicalReference);
45-
return new ExcerptToken(x.kind, x.text, canonicalReference);
42+
this._excerptTokens = options.excerptTokens.map(token => {
43+
const canonicalReference: DeclarationReference | undefined = token.canonicalReference === undefined ? undefined :
44+
DeclarationReference.parse(token.canonicalReference);
45+
return new ExcerptToken(token.kind, token.text, canonicalReference);
4646
});
4747
this._excerpt = new Excerpt(this.excerptTokens, { startIndex: 0, endIndex: this.excerptTokens.length });
4848
}

apps/api-extractor-model/src/mixins/Excerpt.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@ import { Text } from '@microsoft/node-core-library';
66

77
/** @public */
88
export const enum ExcerptTokenKind {
9+
/**
10+
* Generic text without any special properties
11+
*/
912
Content = 'Content',
1013

11-
// Soon we will support hyperlinks to other API declarations
14+
/**
15+
* A reference to an API declaration
16+
*/
1217
Reference = 'Reference'
1318
}
1419

apps/api-extractor/src/generators/ApiModelGenerator.ts

Lines changed: 45 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,13 @@ export class ApiModelGenerator {
9898
return; // trim out private declarations
9999
}
100100

101-
const releaseTag: ReleaseTag = this._collector.fetchMetadata(astDeclaration).effectiveReleaseTag;
101+
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
102+
if (declarationMetadata.isAncillary) {
103+
// Skip ancillary declarations; we will process them with the main declaration
104+
return;
105+
}
106+
107+
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
102108
if (releaseTag === ReleaseTag.Internal || releaseTag === ReleaseTag.Alpha) {
103109
return; // trim out items marked as "@internal" or "@alpha"
104110
}
@@ -206,11 +212,7 @@ export class ApiModelGenerator {
206212

207213
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, callSignature.parameters);
208214

209-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
210-
referenceGenerator: this._referenceGenerator,
211-
startingNode: astDeclaration.declaration,
212-
nodesToCapture
213-
});
215+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
214216
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
215217
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
216218
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -245,11 +247,7 @@ export class ApiModelGenerator {
245247
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture,
246248
constructorDeclaration.parameters);
247249

248-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
249-
referenceGenerator: this._referenceGenerator,
250-
startingNode: astDeclaration.declaration,
251-
nodesToCapture
252-
});
250+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
253251
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
254252
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
255253
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -300,12 +298,7 @@ export class ApiModelGenerator {
300298
}
301299
}
302300

303-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
304-
referenceGenerator: this._referenceGenerator,
305-
startingNode: astDeclaration.declaration,
306-
stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{"
307-
nodesToCapture
308-
});
301+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
309302
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
310303
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
311304
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -349,11 +342,7 @@ export class ApiModelGenerator {
349342

350343
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, constructSignature.parameters);
351344

352-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
353-
referenceGenerator: this._referenceGenerator,
354-
startingNode: astDeclaration.declaration,
355-
nodesToCapture
356-
});
345+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
357346
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
358347
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
359348
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -381,11 +370,7 @@ export class ApiModelGenerator {
381370
let apiEnum: ApiEnum | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiEnum;
382371

383372
if (apiEnum === undefined) {
384-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
385-
referenceGenerator: this._referenceGenerator,
386-
startingNode: astDeclaration.declaration,
387-
stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation // FirstPunctuation = "{"
388-
});
373+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, []);
389374
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
390375
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
391376
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -413,11 +398,7 @@ export class ApiModelGenerator {
413398
const initializerTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
414399
nodesToCapture.push({ node: enumMember.initializer, tokenRange: initializerTokenRange });
415400

416-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
417-
referenceGenerator: this._referenceGenerator,
418-
startingNode: astDeclaration.declaration,
419-
nodesToCapture
420-
});
401+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
421402
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
422403
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
423404
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -459,11 +440,7 @@ export class ApiModelGenerator {
459440
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture,
460441
functionDeclaration.parameters);
461442

462-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
463-
referenceGenerator: this._referenceGenerator,
464-
startingNode: astDeclaration.declaration,
465-
nodesToCapture
466-
});
443+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
467444
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
468445
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
469446
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -505,11 +482,7 @@ export class ApiModelGenerator {
505482

506483
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, indexSignature.parameters);
507484

508-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
509-
referenceGenerator: this._referenceGenerator,
510-
startingNode: astDeclaration.declaration,
511-
nodesToCapture
512-
});
485+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
513486
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
514487
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
515488
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -555,12 +528,7 @@ export class ApiModelGenerator {
555528
}
556529
}
557530

558-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
559-
referenceGenerator: this._referenceGenerator,
560-
startingNode: astDeclaration.declaration,
561-
stopBeforeChildKind: ts.SyntaxKind.FirstPunctuation, // FirstPunctuation = "{"
562-
nodesToCapture
563-
});
531+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
564532
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
565533
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
566534
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -604,11 +572,7 @@ export class ApiModelGenerator {
604572

605573
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodDeclaration.parameters);
606574

607-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
608-
referenceGenerator: this._referenceGenerator,
609-
startingNode: astDeclaration.declaration,
610-
nodesToCapture
611-
});
575+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
612576
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
613577
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
614578
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -656,11 +620,7 @@ export class ApiModelGenerator {
656620

657621
const parameters: IApiParameterOptions[] = this._captureParameters(nodesToCapture, methodSignature.parameters);
658622

659-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
660-
referenceGenerator: this._referenceGenerator,
661-
startingNode: astDeclaration.declaration,
662-
nodesToCapture
663-
});
623+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
664624
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
665625
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
666626
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -689,11 +649,7 @@ export class ApiModelGenerator {
689649
let apiNamespace: ApiNamespace | undefined = parentApiItem.tryGetMemberByKey(containerKey) as ApiNamespace;
690650

691651
if (apiNamespace === undefined) {
692-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
693-
referenceGenerator: this._referenceGenerator,
694-
startingNode: astDeclaration.declaration,
695-
stopBeforeChildKind: ts.SyntaxKind.ModuleBlock // ModuleBlock = the "{ ... }" block
696-
});
652+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, []);
697653
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
698654
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
699655
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -725,11 +681,7 @@ export class ApiModelGenerator {
725681
const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
726682
nodesToCapture.push({ node: propertyDeclaration.type, tokenRange: propertyTypeTokenRange });
727683

728-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
729-
referenceGenerator: this._referenceGenerator,
730-
startingNode: astDeclaration.declaration,
731-
nodesToCapture
732-
});
684+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
733685
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
734686
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
735687
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -759,11 +711,7 @@ export class ApiModelGenerator {
759711
const propertyTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
760712
nodesToCapture.push({ node: propertySignature.type, tokenRange: propertyTypeTokenRange });
761713

762-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
763-
referenceGenerator: this._referenceGenerator,
764-
startingNode: astDeclaration.declaration,
765-
nodesToCapture
766-
});
714+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
767715
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
768716
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
769717
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -804,11 +752,7 @@ export class ApiModelGenerator {
804752
const typeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
805753
nodesToCapture.push({ node: typeAliasDeclaration.type, tokenRange: typeTokenRange });
806754

807-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
808-
referenceGenerator: this._referenceGenerator,
809-
startingNode: astDeclaration.declaration,
810-
nodesToCapture
811-
});
755+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
812756
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
813757
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
814758
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -844,11 +788,7 @@ export class ApiModelGenerator {
844788
const variableTypeTokenRange: IExcerptTokenRange = ExcerptBuilder.createEmptyTokenRange();
845789
nodesToCapture.push({ node: variableDeclaration.type, tokenRange: variableTypeTokenRange });
846790

847-
const excerptTokens: IExcerptToken[] = ExcerptBuilder.build({
848-
referenceGenerator: this._referenceGenerator,
849-
startingNode: astDeclaration.declaration,
850-
nodesToCapture
851-
});
791+
const excerptTokens: IExcerptToken[] = this._buildExcerptTokens(astDeclaration, nodesToCapture);
852792
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
853793
const docComment: tsdoc.DocComment | undefined = declarationMetadata.tsdocComment;
854794
const releaseTag: ReleaseTag = declarationMetadata.effectiveReleaseTag;
@@ -859,6 +799,28 @@ export class ApiModelGenerator {
859799
}
860800
}
861801

802+
/**
803+
* @param nodesToCapture - A list of child nodes whose token ranges we want to capture
804+
*/
805+
private _buildExcerptTokens(astDeclaration: AstDeclaration,
806+
nodesToCapture: IExcerptBuilderNodeToCapture[]): IExcerptToken[] {
807+
808+
const excerptTokens: IExcerptToken[] = [];
809+
810+
// Build the main declaration
811+
ExcerptBuilder.addDeclaration(excerptTokens, astDeclaration, nodesToCapture, this._referenceGenerator);
812+
813+
const declarationMetadata: DeclarationMetadata = this._collector.fetchMetadata(astDeclaration);
814+
815+
// Add any ancillary declarations
816+
for (const ancillaryDeclaration of declarationMetadata.ancillaryDeclarations) {
817+
ExcerptBuilder.addBlankLine(excerptTokens);
818+
ExcerptBuilder.addDeclaration(excerptTokens, ancillaryDeclaration, nodesToCapture, this._referenceGenerator);
819+
}
820+
821+
return excerptTokens;
822+
}
823+
862824
private _captureTypeParameters(nodesToCapture: IExcerptBuilderNodeToCapture[], typeParameterNodes:
863825
ts.NodeArray<ts.TypeParameterDeclaration> | undefined): IApiTypeParameterOptions[] {
864826

apps/api-extractor/src/generators/ExcerptBuilder.ts

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111

1212
import { Span } from '../analyzer/Span';
1313
import { DeclarationReferenceGenerator } from './DeclarationReferenceGenerator';
14+
import { AstDeclaration } from '../analyzer/AstDeclaration';
1415

1516
/**
1617
* Used to provide ExcerptBuilder with a list of nodes whose token range we want to capture.
@@ -28,10 +29,11 @@ export interface IExcerptBuilderNodeToCapture {
2829
}
2930

3031
/**
31-
* Options for ExcerptBuilder
32+
* Internal state for ExcerptBuilder
3233
*/
33-
export interface ISignatureBuilderOptions {
34+
interface IBuildSpanState {
3435
referenceGenerator: DeclarationReferenceGenerator;
36+
3537
/**
3638
* The AST node that we will traverse to extract tokens
3739
*/
@@ -46,20 +48,6 @@ export interface ISignatureBuilderOptions {
4648
* For example, suppose the signature is `interface X: Y { z: string }`. The token `{` has syntax kind
4749
* `ts.SyntaxKind.FirstPunctuation`, so we can specify that to truncate the excerpt to `interface X: Y`.
4850
*/
49-
stopBeforeChildKind?: ts.SyntaxKind;
50-
51-
/**
52-
* A list of child nodes whose token ranges we want to capture
53-
*/
54-
nodesToCapture?: IExcerptBuilderNodeToCapture[];
55-
}
56-
57-
/**
58-
* Internal state for ExcerptBuilder
59-
*/
60-
interface IBuildSpanState {
61-
referenceGenerator: DeclarationReferenceGenerator;
62-
startingNode: ts.Node;
6351
stopBeforeChildKind: ts.SyntaxKind | undefined;
6452

6553
tokenRangesByNode: Map<ts.Node, IExcerptTokenRange>;
@@ -73,27 +61,61 @@ interface IBuildSpanState {
7361
}
7462

7563
export class ExcerptBuilder {
76-
public static build(options: ISignatureBuilderOptions): IExcerptToken[] {
77-
const span: Span = new Span(options.startingNode);
64+
/**
65+
* Appends a blank line to the `excerptTokens` list.
66+
* @param excerptTokens - The target token list to append to
67+
*/
68+
public static addBlankLine(excerptTokens: IExcerptToken[]): void {
69+
let newlines: string = '\n\n';
70+
// If the existing text already ended with a newline, then only append one newline
71+
if (excerptTokens.length > 0) {
72+
const previousText: string = excerptTokens[excerptTokens.length - 1].text;
73+
if (/\n$/.test(previousText)) {
74+
newlines = '\n';
75+
}
76+
}
77+
excerptTokens.push({ kind: ExcerptTokenKind.Content, text: newlines });
78+
}
79+
80+
/**
81+
* Appends the signature for the specified `AstDeclaration` to the `excerptTokens` list.
82+
* @param excerptTokens - The target token list to append to
83+
* @param nodesToCapture - A list of child nodes whose token ranges we want to capture
84+
*/
85+
public static addDeclaration(excerptTokens: IExcerptToken[], astDeclaration: AstDeclaration,
86+
nodesToCapture: IExcerptBuilderNodeToCapture[], referenceGenerator: DeclarationReferenceGenerator): void {
87+
88+
let stopBeforeChildKind: ts.SyntaxKind | undefined = undefined;
89+
90+
switch (astDeclaration.declaration.kind) {
91+
case ts.SyntaxKind.ClassDeclaration:
92+
case ts.SyntaxKind.EnumDeclaration:
93+
case ts.SyntaxKind.InterfaceDeclaration:
94+
// FirstPunctuation = "{"
95+
stopBeforeChildKind = ts.SyntaxKind.FirstPunctuation;
96+
break;
97+
case ts.SyntaxKind.ModuleDeclaration:
98+
// ModuleBlock = the "{ ... }" block
99+
stopBeforeChildKind = ts.SyntaxKind.ModuleBlock;
100+
break;
101+
}
102+
103+
const span: Span = new Span(astDeclaration.declaration);
78104

79105
const tokenRangesByNode: Map<ts.Node, IExcerptTokenRange> = new Map<ts.Node, IExcerptTokenRange>();
80-
for (const excerpt of options.nodesToCapture || []) {
106+
for (const excerpt of nodesToCapture || []) {
81107
if (excerpt.node) {
82108
tokenRangesByNode.set(excerpt.node, excerpt.tokenRange);
83109
}
84110
}
85111

86-
const excerptTokens: IExcerptToken[] = [];
87-
88112
ExcerptBuilder._buildSpan(excerptTokens, span, {
89-
referenceGenerator: options.referenceGenerator,
90-
startingNode: options.startingNode,
91-
stopBeforeChildKind: options.stopBeforeChildKind,
113+
referenceGenerator: referenceGenerator,
114+
startingNode: span.node,
115+
stopBeforeChildKind,
92116
tokenRangesByNode,
93117
disableMergingForNextToken: false
94118
});
95-
96-
return excerptTokens;
97119
}
98120

99121
public static createEmptyTokenRange(): IExcerptTokenRange {

0 commit comments

Comments
 (0)