Skip to content

Commit 42ab8f0

Browse files
committed
WIP
1 parent 0a38e48 commit 42ab8f0

37 files changed

+946
-171
lines changed

apps/api-extractor/src/api/ExtractorConfig.ts

+14
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,10 @@ interface IExtractorConfigParameters {
158158
apiReportEnabled: boolean;
159159
reportFilePath: string;
160160
reportTempFilePath: string;
161+
apiReportIncludeForgottenExports: boolean;
161162
docModelEnabled: boolean;
162163
apiJsonFilePath: string;
164+
docModelIncludeForgottenExports: boolean;
163165
rollupEnabled: boolean;
164166
untrimmedFilePath: string;
165167
alphaTrimmedFilePath: string;
@@ -246,11 +248,15 @@ export class ExtractorConfig {
246248
public readonly reportFilePath: string;
247249
/** The `reportTempFolder` path combined with the `reportFileName`. */
248250
public readonly reportTempFilePath: string;
251+
/** {@inheritDoc IConfigApiReport.includeForgottenExports} */
252+
public readonly apiReportIncludeForgottenExports: boolean;
249253

250254
/** {@inheritDoc IConfigDocModel.enabled} */
251255
public readonly docModelEnabled: boolean;
252256
/** {@inheritDoc IConfigDocModel.apiJsonFilePath} */
253257
public readonly apiJsonFilePath: string;
258+
/** {@inheritDoc IConfigDocModel.includeForgottenExports} */
259+
public readonly docModelIncludeForgottenExports: boolean;
254260

255261
/** {@inheritDoc IConfigDtsRollup.enabled} */
256262
public readonly rollupEnabled: boolean;
@@ -307,8 +313,10 @@ export class ExtractorConfig {
307313
this.apiReportEnabled = parameters.apiReportEnabled;
308314
this.reportFilePath = parameters.reportFilePath;
309315
this.reportTempFilePath = parameters.reportTempFilePath;
316+
this.apiReportIncludeForgottenExports = parameters.apiReportIncludeForgottenExports;
310317
this.docModelEnabled = parameters.docModelEnabled;
311318
this.apiJsonFilePath = parameters.apiJsonFilePath;
319+
this.docModelIncludeForgottenExports = parameters.docModelIncludeForgottenExports;
312320
this.rollupEnabled = parameters.rollupEnabled;
313321
this.untrimmedFilePath = parameters.untrimmedFilePath;
314322
this.alphaTrimmedFilePath = parameters.alphaTrimmedFilePath;
@@ -848,6 +856,7 @@ export class ExtractorConfig {
848856
let apiReportEnabled: boolean = false;
849857
let reportFilePath: string = '';
850858
let reportTempFilePath: string = '';
859+
let apiReportIncludeForgottenExports: boolean = false;
851860
if (configObject.apiReport) {
852861
apiReportEnabled = !!configObject.apiReport.enabled;
853862

@@ -879,17 +888,20 @@ export class ExtractorConfig {
879888

880889
reportFilePath = path.join(reportFolder, reportFilename);
881890
reportTempFilePath = path.join(reportTempFolder, reportFilename);
891+
apiReportIncludeForgottenExports = !!configObject.apiReport.includeForgottenExports;
882892
}
883893

884894
let docModelEnabled: boolean = false;
885895
let apiJsonFilePath: string = '';
896+
let docModelIncludeForgottenExports: boolean = false;
886897
if (configObject.docModel) {
887898
docModelEnabled = !!configObject.docModel.enabled;
888899
apiJsonFilePath = ExtractorConfig._resolvePathWithTokens(
889900
'apiJsonFilePath',
890901
configObject.docModel.apiJsonFilePath,
891902
tokenContext
892903
);
904+
docModelIncludeForgottenExports = !!configObject.docModel.includeForgottenExports;
893905
}
894906

895907
let tsdocMetadataEnabled: boolean = false;
@@ -993,8 +1005,10 @@ export class ExtractorConfig {
9931005
apiReportEnabled,
9941006
reportFilePath,
9951007
reportTempFilePath,
1008+
apiReportIncludeForgottenExports,
9961009
docModelEnabled,
9971010
apiJsonFilePath,
1011+
docModelIncludeForgottenExports,
9981012
rollupEnabled,
9991013
untrimmedFilePath,
10001014
alphaTrimmedFilePath,

apps/api-extractor/src/api/IConfigFile.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,17 @@ export interface IConfigApiReport {
9696
* prepend a folder token such as `<projectFolder>`.
9797
*/
9898
reportTempFolder?: string;
99+
100+
/**
101+
* Whether "forgotten exports" should be included in the API report file.
102+
*
103+
* @remarks
104+
* Forgotten exports are declarations flagged with `ae-forgotten-export` warnings. See
105+
* https://api-extractor.com/pages/messages/ae-forgotten-export/ to learn more.
106+
*
107+
* @defaultValue `false`
108+
*/
109+
includeForgottenExports?: boolean;
99110
}
100111

101112
/**
@@ -120,6 +131,17 @@ export interface IConfigDocModel {
120131
* prepend a folder token such as `<projectFolder>`.
121132
*/
122133
apiJsonFilePath?: string;
134+
135+
/**
136+
* Whether "forgotten exports" should be included in the doc model file.
137+
*
138+
* @remarks
139+
* Forgotten exports are declarations flagged with `ae-forgotten-export` warnings. See
140+
* https://api-extractor.com/pages/messages/ae-forgotten-export/ to learn more.
141+
*
142+
* @defaultValue `false`
143+
*/
144+
includeForgottenExports?: boolean;
123145
}
124146

125147
/**
@@ -376,7 +398,7 @@ export interface IConfigFile {
376398
testMode?: boolean;
377399

378400
/**
379-
* Specifies how API Extractor sorts members of an enum when generating api.json.
401+
* Specifies how API Extractor sorts members of an enum when generating the .api.json file.
380402
*
381403
* @remarks
382404
* By default, the output files will be sorted alphabetically, which is "by-name".

apps/api-extractor/src/collector/Collector.ts

+47-40
Original file line numberDiff line numberDiff line change
@@ -246,28 +246,23 @@ export class Collector {
246246
this.workingPackage.tsdocComment = this.workingPackage.tsdocParserContext!.docComment;
247247
}
248248

249-
const exportedAstEntities: AstEntity[] = [];
250-
251-
// Create a CollectorEntity for each top-level export
252-
253249
const astModuleExportInfo: AstModuleExportInfo =
254250
this.astSymbolTable.fetchAstModuleExportInfo(astEntryPoint);
255251

252+
// Create a CollectorEntity for each top-level export.
253+
const processedAstEntities: AstEntity[] = [];
256254
for (const [exportName, astEntity] of astModuleExportInfo.exportedLocalEntities) {
257255
this._createCollectorEntity(astEntity, exportName);
258-
259-
exportedAstEntities.push(astEntity);
256+
processedAstEntities.push(astEntity);
260257
}
261258

262-
// Create a CollectorEntity for each indirectly referenced export.
263-
// Note that we do this *after* the above loop, so that references to exported AstSymbols
264-
// are encountered first as exports.
265-
const alreadySeenAstSymbols: Set<AstSymbol> = new Set<AstSymbol>();
266-
for (const exportedAstEntity of exportedAstEntities) {
267-
this._createEntityForIndirectReferences(exportedAstEntity, alreadySeenAstSymbols);
268-
269-
if (exportedAstEntity instanceof AstSymbol) {
270-
this.fetchSymbolMetadata(exportedAstEntity);
259+
// Recursively create the remaining CollectorEntities after the top-level entities
260+
// have been processed.
261+
const alreadySeenAstEntities: Set<AstEntity> = new Set<AstEntity>();
262+
for (const astEntity of processedAstEntities) {
263+
this._recursivelyCreateEntities(astEntity, alreadySeenAstEntities);
264+
if (astEntity instanceof AstSymbol) {
265+
this.fetchSymbolMetadata(astEntity);
271266
}
272267
}
273268

@@ -414,7 +409,11 @@ export class Collector {
414409
return overloadIndex;
415410
}
416411

417-
private _createCollectorEntity(astEntity: AstEntity, exportedName: string | undefined): CollectorEntity {
412+
private _createCollectorEntity(
413+
astEntity: AstEntity,
414+
exportName?: string,
415+
parent?: CollectorEntity
416+
): CollectorEntity {
418417
let entity: CollectorEntity | undefined = this._entitiesByAstEntity.get(astEntity);
419418

420419
if (!entity) {
@@ -425,50 +424,54 @@ export class Collector {
425424
this._collectReferenceDirectives(astEntity);
426425
}
427426

428-
if (exportedName) {
429-
entity.addExportName(exportedName);
427+
if (exportName) {
428+
if (parent) {
429+
entity.addLocalExportName(exportName, parent);
430+
} else {
431+
entity.addExportName(exportName);
432+
}
430433
}
431434

432435
return entity;
433436
}
434437

435-
private _createEntityForIndirectReferences(
436-
astEntity: AstEntity,
437-
alreadySeenAstEntities: Set<AstEntity>
438-
): void {
439-
if (alreadySeenAstEntities.has(astEntity)) {
440-
return;
441-
}
438+
private _recursivelyCreateEntities(astEntity: AstEntity, alreadySeenAstEntities: Set<AstEntity>): void {
439+
if (alreadySeenAstEntities.has(astEntity)) return;
442440
alreadySeenAstEntities.add(astEntity);
443441

444442
if (astEntity instanceof AstSymbol) {
445443
astEntity.forEachDeclarationRecursive((astDeclaration: AstDeclaration) => {
446444
for (const referencedAstEntity of astDeclaration.referencedAstEntities) {
447445
if (referencedAstEntity instanceof AstSymbol) {
448-
// We only create collector entities for root-level symbols.
449-
// For example, if a symbols is nested inside a namespace, only the root-level namespace
450-
// get a collector entity
446+
// We only create collector entities for root-level symbols. For example, if a symbol is
447+
// nested inside a namespace, only the namespace gets a collector entity. Note that this
448+
// is not true for AstNamespaceImports below.
451449
if (referencedAstEntity.parentAstSymbol === undefined) {
452-
this._createCollectorEntity(referencedAstEntity, undefined);
450+
this._createCollectorEntity(referencedAstEntity);
453451
}
454452
} else {
455-
this._createCollectorEntity(referencedAstEntity, undefined);
453+
this._createCollectorEntity(referencedAstEntity);
456454
}
457455

458-
this._createEntityForIndirectReferences(referencedAstEntity, alreadySeenAstEntities);
456+
this._recursivelyCreateEntities(referencedAstEntity, alreadySeenAstEntities);
459457
}
460458
});
461459
}
462460

463461
if (astEntity instanceof AstNamespaceImport) {
464462
const astModuleExportInfo: AstModuleExportInfo = astEntity.fetchAstModuleExportInfo(this);
463+
const parentEntity: CollectorEntity | undefined = this._entitiesByAstEntity.get(astEntity);
464+
if (!parentEntity) {
465+
// This should never happen, as we've already created entities for all AstNamespaceImports.
466+
throw new InternalError(
467+
`Failed to get CollectorEntity for AstNamespaceImport with namespace name "${astEntity.namespaceName}"`
468+
);
469+
}
465470

466-
for (const exportedEntity of astModuleExportInfo.exportedLocalEntities.values()) {
467-
// Create a CollectorEntity for each top-level export of AstImportInternal entity
468-
const entity: CollectorEntity = this._createCollectorEntity(exportedEntity, undefined);
469-
entity.addAstNamespaceImports(astEntity);
470-
471-
this._createEntityForIndirectReferences(exportedEntity, alreadySeenAstEntities);
471+
for (const [localExportName, localAstEntity] of astModuleExportInfo.exportedLocalEntities) {
472+
// Create a CollectorEntity for each local export within an AstNamespaceImport entity.
473+
this._createCollectorEntity(localAstEntity, localExportName, parentEntity);
474+
this._recursivelyCreateEntities(localAstEntity, alreadySeenAstEntities);
472475
}
473476
}
474477
}
@@ -815,7 +818,7 @@ export class Collector {
815818
// Don't report missing release tags for forgotten exports
816819
const astSymbol: AstSymbol = astDeclaration.astSymbol;
817820
const entity: CollectorEntity | undefined = this._entitiesByAstEntity.get(astSymbol.rootAstSymbol);
818-
if (entity && entity.consumable) {
821+
if (entity && entity.exported) {
819822
// We also don't report errors for the default export of an entry point, since its doc comment
820823
// isn't easy to obtain from the .d.ts file
821824
if (astSymbol.rootAstSymbol.localName !== '_default') {
@@ -826,10 +829,14 @@ export class Collector {
826829
astSymbol
827830
);
828831
}
832+
833+
// All internal, exported APIs default to public if no release tag is specified.
834+
options.effectiveReleaseTag = ReleaseTag.Public;
829835
}
836+
} else {
837+
// All external APIs default to public if no release tag is specified.
838+
options.effectiveReleaseTag = ReleaseTag.Public;
830839
}
831-
832-
options.effectiveReleaseTag = ReleaseTag.Public;
833840
}
834841

835842
const apiItemMetadata: ApiItemMetadata = new ApiItemMetadata(options);

0 commit comments

Comments
 (0)