Skip to content

Commit 82e5859

Browse files
authored
[api-extractor] Add a docModel.releaseTagsToTrim property to api-extractor.json to specify which release tags should be trimmed when the doc model is produced. (#5113)
* Add a docModel.releaseTagsToTrim property to api-extractor.json to specify which release tags should be trimmed when the doc model is produced. * Add a test for doc model trimming. * fixup! Add a test for doc model trimming. * fixup! Add a docModel.releaseTagsToTrim property to api-extractor.json to specify which release tags should be trimmed when the doc model is produced.
1 parent 17d3b74 commit 82e5859

25 files changed

+656
-50
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ These GitHub repositories provide supplementary resources for Rush Stack:
152152
| [/build-tests/api-extractor-test-02](./build-tests/api-extractor-test-02/) | Building this project is a regression test for api-extractor |
153153
| [/build-tests/api-extractor-test-03](./build-tests/api-extractor-test-03/) | Building this project is a regression test for api-extractor |
154154
| [/build-tests/api-extractor-test-04](./build-tests/api-extractor-test-04/) | Building this project is a regression test for api-extractor |
155+
| [/build-tests/api-extractor-test-05](./build-tests/api-extractor-test-05/) | Building this project is a regression test for api-extractor |
155156
| [/build-tests/eslint-7-11-test](./build-tests/eslint-7-11-test/) | This project contains a build test to validate ESLint 7.11.0 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) |
156157
| [/build-tests/eslint-7-7-test](./build-tests/eslint-7-7-test/) | This project contains a build test to validate ESLint 7.7.0 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) |
157158
| [/build-tests/eslint-7-test](./build-tests/eslint-7-test/) | This project contains a build test to validate ESLint 7 compatibility with the latest version of @rushstack/eslint-config (and by extension, the ESLint plugin) |

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export class Extractor {
254254
const collector: Collector = new Collector({
255255
program: compilerState.program as ts.Program,
256256
messageRouter,
257-
extractorConfig: extractorConfig,
257+
extractorConfig,
258258
sourceMapper
259259
});
260260

@@ -263,14 +263,14 @@ export class Extractor {
263263
DocCommentEnhancer.analyze(collector);
264264
ValidationEnhancer.analyze(collector);
265265

266-
const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector);
266+
const modelBuilder: ApiModelGenerator = new ApiModelGenerator(collector, extractorConfig);
267267
const apiPackage: ApiPackage = modelBuilder.buildApiPackage();
268268

269269
if (messageRouter.showDiagnostics) {
270270
messageRouter.logDiagnostic(''); // skip a line after any diagnostic messages
271271
}
272272

273-
if (extractorConfig.docModelEnabled) {
273+
if (modelBuilder.docModelEnabled) {
274274
messageRouter.logVerbose(
275275
ConsoleMessageId.WritingDocModelFile,
276276
'Writing: ' + extractorConfig.apiJsonFilePath

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

Lines changed: 114 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import {
1717
NewlineKind
1818
} from '@rushstack/node-core-library';
1919
import { type IRigConfig, RigConfig } from '@rushstack/rig-package';
20+
import { EnumMemberOrder, ReleaseTag } from '@microsoft/api-extractor-model';
21+
import { TSDocConfiguration } from '@microsoft/tsdoc';
22+
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
2023

2124
import type {
2225
ApiReportVariant,
@@ -26,9 +29,7 @@ import type {
2629
} from './IConfigFile';
2730
import { PackageMetadataManager } from '../analyzer/PackageMetadataManager';
2831
import { MessageRouter } from '../collector/MessageRouter';
29-
import { EnumMemberOrder } from '@microsoft/api-extractor-model';
30-
import { TSDocConfiguration } from '@microsoft/tsdoc';
31-
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
32+
import type { IApiModelGenerationOptions } from '../generators/ApiModelGenerator';
3233

3334
import apiExtractorSchema from '../schemas/api-extractor.schema.json';
3435

@@ -186,7 +187,7 @@ interface IExtractorConfigParameters {
186187
reportFolder: string;
187188
reportTempFolder: string;
188189
apiReportIncludeForgottenExports: boolean;
189-
docModelEnabled: boolean;
190+
docModelGenerationOptions: IApiModelGenerationOptions | undefined;
190191
apiJsonFilePath: string;
191192
docModelIncludeForgottenExports: boolean;
192193
projectFolderUrl: string | undefined;
@@ -305,8 +306,11 @@ export class ExtractorConfig {
305306
/** {@inheritDoc IConfigApiReport.includeForgottenExports} */
306307
public readonly apiReportIncludeForgottenExports: boolean;
307308

308-
/** {@inheritDoc IConfigDocModel.enabled} */
309-
public readonly docModelEnabled: boolean;
309+
/**
310+
* If specified, the doc model is enabled and the specified options will be used.
311+
* @beta
312+
*/
313+
public readonly docModelGenerationOptions: IApiModelGenerationOptions | undefined;
310314
/** {@inheritDoc IConfigDocModel.apiJsonFilePath} */
311315
public readonly apiJsonFilePath: string;
312316
/** {@inheritDoc IConfigDocModel.includeForgottenExports} */
@@ -357,38 +361,70 @@ export class ExtractorConfig {
357361
/** {@inheritDoc IConfigFile.enumMemberOrder} */
358362
public readonly enumMemberOrder: EnumMemberOrder;
359363

360-
private constructor(parameters: IExtractorConfigParameters) {
361-
this.projectFolder = parameters.projectFolder;
362-
this.packageJson = parameters.packageJson;
363-
this.packageFolder = parameters.packageFolder;
364-
this.mainEntryPointFilePath = parameters.mainEntryPointFilePath;
365-
this.bundledPackages = parameters.bundledPackages;
366-
this.tsconfigFilePath = parameters.tsconfigFilePath;
367-
this.overrideTsconfig = parameters.overrideTsconfig;
368-
this.skipLibCheck = parameters.skipLibCheck;
369-
this.apiReportEnabled = parameters.apiReportEnabled;
370-
this.apiReportIncludeForgottenExports = parameters.apiReportIncludeForgottenExports;
371-
this.reportConfigs = parameters.reportConfigs;
372-
this.reportFolder = parameters.reportFolder;
373-
this.reportTempFolder = parameters.reportTempFolder;
374-
this.docModelEnabled = parameters.docModelEnabled;
375-
this.apiJsonFilePath = parameters.apiJsonFilePath;
376-
this.docModelIncludeForgottenExports = parameters.docModelIncludeForgottenExports;
377-
this.projectFolderUrl = parameters.projectFolderUrl;
378-
this.rollupEnabled = parameters.rollupEnabled;
379-
this.untrimmedFilePath = parameters.untrimmedFilePath;
380-
this.alphaTrimmedFilePath = parameters.alphaTrimmedFilePath;
381-
this.betaTrimmedFilePath = parameters.betaTrimmedFilePath;
382-
this.publicTrimmedFilePath = parameters.publicTrimmedFilePath;
383-
this.omitTrimmingComments = parameters.omitTrimmingComments;
384-
this.tsdocMetadataEnabled = parameters.tsdocMetadataEnabled;
385-
this.tsdocMetadataFilePath = parameters.tsdocMetadataFilePath;
386-
this.tsdocConfigFile = parameters.tsdocConfigFile;
387-
this.tsdocConfiguration = parameters.tsdocConfiguration;
388-
this.newlineKind = parameters.newlineKind;
389-
this.messages = parameters.messages;
390-
this.testMode = parameters.testMode;
391-
this.enumMemberOrder = parameters.enumMemberOrder;
364+
private constructor({
365+
projectFolder,
366+
packageJson,
367+
packageFolder,
368+
mainEntryPointFilePath,
369+
bundledPackages,
370+
tsconfigFilePath,
371+
overrideTsconfig,
372+
skipLibCheck,
373+
apiReportEnabled,
374+
apiReportIncludeForgottenExports,
375+
reportConfigs,
376+
reportFolder,
377+
reportTempFolder,
378+
docModelGenerationOptions,
379+
apiJsonFilePath,
380+
docModelIncludeForgottenExports,
381+
projectFolderUrl,
382+
rollupEnabled,
383+
untrimmedFilePath,
384+
alphaTrimmedFilePath,
385+
betaTrimmedFilePath,
386+
publicTrimmedFilePath,
387+
omitTrimmingComments,
388+
tsdocMetadataEnabled,
389+
tsdocMetadataFilePath,
390+
tsdocConfigFile,
391+
tsdocConfiguration,
392+
newlineKind,
393+
messages,
394+
testMode,
395+
enumMemberOrder
396+
}: IExtractorConfigParameters) {
397+
this.projectFolder = projectFolder;
398+
this.packageJson = packageJson;
399+
this.packageFolder = packageFolder;
400+
this.mainEntryPointFilePath = mainEntryPointFilePath;
401+
this.bundledPackages = bundledPackages;
402+
this.tsconfigFilePath = tsconfigFilePath;
403+
this.overrideTsconfig = overrideTsconfig;
404+
this.skipLibCheck = skipLibCheck;
405+
this.apiReportEnabled = apiReportEnabled;
406+
this.apiReportIncludeForgottenExports = apiReportIncludeForgottenExports;
407+
this.reportConfigs = reportConfigs;
408+
this.reportFolder = reportFolder;
409+
this.reportTempFolder = reportTempFolder;
410+
this.docModelGenerationOptions = docModelGenerationOptions;
411+
this.apiJsonFilePath = apiJsonFilePath;
412+
this.docModelIncludeForgottenExports = docModelIncludeForgottenExports;
413+
this.projectFolderUrl = projectFolderUrl;
414+
this.rollupEnabled = rollupEnabled;
415+
this.untrimmedFilePath = untrimmedFilePath;
416+
this.alphaTrimmedFilePath = alphaTrimmedFilePath;
417+
this.betaTrimmedFilePath = betaTrimmedFilePath;
418+
this.publicTrimmedFilePath = publicTrimmedFilePath;
419+
this.omitTrimmingComments = omitTrimmingComments;
420+
this.tsdocMetadataEnabled = tsdocMetadataEnabled;
421+
this.tsdocMetadataFilePath = tsdocMetadataFilePath;
422+
this.tsdocConfigFile = tsdocConfigFile;
423+
this.tsdocConfiguration = tsdocConfiguration;
424+
this.newlineKind = newlineKind;
425+
this.messages = messages;
426+
this.testMode = testMode;
427+
this.enumMemberOrder = enumMemberOrder;
392428
}
393429

394430
/**
@@ -998,19 +1034,55 @@ export class ExtractorConfig {
9981034
}
9991035
}
10001036

1001-
let docModelEnabled: boolean = false;
1037+
let docModelGenerationOptions: IApiModelGenerationOptions | undefined = undefined;
10021038
let apiJsonFilePath: string = '';
10031039
let docModelIncludeForgottenExports: boolean = false;
10041040
let projectFolderUrl: string | undefined;
1005-
if (configObject.docModel) {
1006-
docModelEnabled = !!configObject.docModel.enabled;
1041+
if (configObject.docModel?.enabled) {
10071042
apiJsonFilePath = ExtractorConfig._resolvePathWithTokens(
10081043
'apiJsonFilePath',
10091044
configObject.docModel.apiJsonFilePath,
10101045
tokenContext
10111046
);
10121047
docModelIncludeForgottenExports = !!configObject.docModel.includeForgottenExports;
10131048
projectFolderUrl = configObject.docModel.projectFolderUrl;
1049+
1050+
const releaseTagsToTrim: Set<ReleaseTag> = new Set<ReleaseTag>();
1051+
const releaseTagsToTrimOption: string[] = configObject.docModel.releaseTagsToTrim || ['@internal'];
1052+
for (const releaseTagToTrim of releaseTagsToTrimOption) {
1053+
let releaseTag: ReleaseTag;
1054+
switch (releaseTagToTrim) {
1055+
case '@internal': {
1056+
releaseTag = ReleaseTag.Internal;
1057+
break;
1058+
}
1059+
1060+
case '@alpha': {
1061+
releaseTag = ReleaseTag.Alpha;
1062+
break;
1063+
}
1064+
1065+
case '@beta': {
1066+
releaseTag = ReleaseTag.Beta;
1067+
break;
1068+
}
1069+
1070+
case '@public': {
1071+
releaseTag = ReleaseTag.Public;
1072+
break;
1073+
}
1074+
1075+
default: {
1076+
throw new Error(`The release tag "${releaseTagToTrim}" is not supported`);
1077+
}
1078+
}
1079+
1080+
releaseTagsToTrim.add(releaseTag);
1081+
}
1082+
1083+
docModelGenerationOptions = {
1084+
releaseTagsToTrim
1085+
};
10141086
}
10151087

10161088
let tsdocMetadataEnabled: boolean = false;
@@ -1116,7 +1188,7 @@ export class ExtractorConfig {
11161188
reportFolder,
11171189
reportTempFolder,
11181190
apiReportIncludeForgottenExports,
1119-
docModelEnabled,
1191+
docModelGenerationOptions,
11201192
apiJsonFilePath,
11211193
docModelIncludeForgottenExports,
11221194
projectFolderUrl,

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,12 @@ export interface IConfigApiReport {
141141
includeForgottenExports?: boolean;
142142
}
143143

144+
/**
145+
* The allowed release tags that can be used to mark API items.
146+
* @public
147+
*/
148+
export type ReleaseTagForTrim = '@internal' | '@alpha' | '@beta' | '@public';
149+
144150
/**
145151
* Configures how the doc model file (*.api.json) will be generated.
146152
*
@@ -188,6 +194,13 @@ export interface IConfigDocModel {
188194
* Can be omitted if you don't need source code links in your API documentation reference.
189195
*/
190196
projectFolderUrl?: string;
197+
198+
/**
199+
* Specifies a list of release tags that will be trimmed from the doc model.
200+
*
201+
* @defaultValue `["@internal"]`
202+
*/
203+
releaseTagsToTrim?: ReleaseTagForTrim[];
191204
}
192205

193206
/**

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

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,45 @@ import { AstNamespaceImport } from '../analyzer/AstNamespaceImport';
4848
import type { AstEntity } from '../analyzer/AstEntity';
4949
import type { AstModule } from '../analyzer/AstModule';
5050
import { TypeScriptInternals } from '../analyzer/TypeScriptInternals';
51+
import type { ExtractorConfig } from '../api/ExtractorConfig';
5152

5253
interface IProcessAstEntityContext {
5354
name: string;
5455
isExported: boolean;
5556
parentApiItem: ApiItemContainerMixin;
5657
}
5758

59+
/**
60+
* @beta
61+
*/
62+
export interface IApiModelGenerationOptions {
63+
/**
64+
* The release tags to trim.
65+
*/
66+
releaseTagsToTrim: Set<ReleaseTag>;
67+
}
68+
5869
export class ApiModelGenerator {
5970
private readonly _collector: Collector;
6071
private readonly _apiModel: ApiModel;
6172
private readonly _referenceGenerator: DeclarationReferenceGenerator;
73+
private readonly _releaseTagsToTrim: Set<ReleaseTag> | undefined;
74+
75+
public readonly docModelEnabled: boolean;
6276

63-
public constructor(collector: Collector) {
77+
public constructor(collector: Collector, extractorConfig: ExtractorConfig) {
6478
this._collector = collector;
6579
this._apiModel = new ApiModel();
6680
this._referenceGenerator = new DeclarationReferenceGenerator(collector);
81+
82+
const apiModelGenerationOptions: IApiModelGenerationOptions | undefined =
83+
extractorConfig.docModelGenerationOptions;
84+
if (apiModelGenerationOptions) {
85+
this._releaseTagsToTrim = apiModelGenerationOptions.releaseTagsToTrim;
86+
this.docModelEnabled = true;
87+
} else {
88+
this.docModelEnabled = false;
89+
}
6790
}
6891

6992
public get apiModel(): ApiModel {
@@ -176,8 +199,8 @@ export class ApiModelGenerator {
176199

177200
const apiItemMetadata: ApiItemMetadata = this._collector.fetchApiItemMetadata(astDeclaration);
178201
const releaseTag: ReleaseTag = apiItemMetadata.effectiveReleaseTag;
179-
if (releaseTag === ReleaseTag.Internal) {
180-
return; // trim out items marked as "@internal"
202+
if (this._releaseTagsToTrim?.has(releaseTag)) {
203+
return;
181204
}
182205

183206
switch (astDeclaration.declaration.kind) {

apps/api-extractor/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ export {
2222
ExtractorConfig
2323
} from './api/ExtractorConfig';
2424

25+
export type { IApiModelGenerationOptions } from './generators/ApiModelGenerator';
26+
2527
export { ExtractorLogLevel } from './api/ExtractorLogLevel';
2628

2729
export {
@@ -42,5 +44,6 @@ export type {
4244
IConfigMessageReportingRule,
4345
IConfigMessageReportingTable,
4446
IExtractorMessagesConfig,
45-
IConfigFile
47+
IConfigFile,
48+
ReleaseTagForTrim
4649
} from './api/IConfigFile';

apps/api-extractor/src/schemas/api-extractor.schema.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@
131131
"projectFolderUrl": {
132132
"description": "The base URL where the project's source code can be viewed on a website such as GitHub or Azure DevOps. This URL path corresponds to the `<projectFolder>` path on disk. This URL is concatenated with the file paths serialized to the doc model to produce URL file paths to individual API items. For example, if the `projectFolderUrl` is \"https://github.com/microsoft/rushstack/tree/main/apps/api-extractor\" and an API item's file path is \"api/ExtractorConfig.ts\", the full URL file path would be \"https://github.com/microsoft/rushstack/tree/main/apps/api-extractor/api/ExtractorConfig.js\". Can be omitted if you don't need source code links in your API documentation reference.",
133133
"type": "string"
134+
},
135+
"releaseTagsToTrim": {
136+
"description": "Specifies a list of release tags that will be trimmed from the doc model. The default value is `[\"@internal\"]`.",
137+
"type": "array",
138+
"items": {
139+
"enum": ["@internal", "@alpha", "@beta", "@public"]
140+
},
141+
"uniqueItems": true
134142
}
135143
},
136144
"required": ["enabled"],
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# This project's outputs are tracked to surface changes to API Extractor rollups during PRs
2+
!dist
3+
dist/*
4+
!dist/*.d.ts
5+
!dist/*.json
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3+
4+
"mainEntryPointFilePath": "<projectFolder>/lib/index.d.ts",
5+
6+
"apiReport": {
7+
"enabled": true
8+
},
9+
10+
"docModel": {
11+
"enabled": true,
12+
"apiJsonFilePath": "<projectFolder>/dist/api-extractor-test-05.api.json",
13+
"releaseTagsToTrim": ["@internal", "@alpha"]
14+
},
15+
16+
"dtsRollup": {
17+
"enabled": true,
18+
19+
"alphaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-alpha.d.ts",
20+
"betaTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-beta.d.ts",
21+
"publicTrimmedFilePath": "<projectFolder>/dist/<unscopedPackageName>-public.d.ts"
22+
},
23+
24+
"testMode": true
25+
}

0 commit comments

Comments
 (0)