Skip to content

Commit

Permalink
refactor: Make the structure of tagsToReport easier to override
Browse files Browse the repository at this point in the history
  • Loading branch information
Josmithr committed Jan 14, 2025
1 parent c2c0dc1 commit 6dab01c
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 65 deletions.
25 changes: 14 additions & 11 deletions apps/api-extractor/src/api/ExtractorConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,13 @@ const defaultApiReportVariants: readonly ApiReportVariant[] = ['complete'];
* Also note that the order of tags in this list is significant, as it determines the order of tags in the report.
* Any changes to this list should be considered breaking.
*/
const defaultTagsToReport: readonly string[] = [
'@sealed',
'@virtual',
'@override',
'@eventProperty',
'@deprecated'
];
const defaultTagsToReport: Readonly<Record<`@${string}`, boolean>> = {
'@sealed': true,
'@virtual': true,
'@override': true,
'@eventProperty': true,
'@deprecated': true
};

interface IExtractorConfigParameters {
projectFolder: string;
Expand All @@ -205,7 +205,7 @@ interface IExtractorConfigParameters {
reportFolder: string;
reportTempFolder: string;
apiReportIncludeForgottenExports: boolean;
tagsToReport: readonly string[];
tagsToReport: Readonly<Record<`@${string}`, boolean>>;
docModelEnabled: boolean;
apiJsonFilePath: string;
docModelIncludeForgottenExports: boolean;
Expand Down Expand Up @@ -302,7 +302,7 @@ export class ExtractorConfig {
/** {@inheritDoc IConfigApiReport.reportTempFolder} */
public readonly reportTempFolder: string;
/** {@inheritDoc IConfigApiReport.tagsToReport} */
public readonly tagsToReport: readonly string[];
public readonly tagsToReport: Readonly<Record<`@${string}`, boolean>>;

/**
* Gets the file path for the "complete" (default) report configuration, if one was specified.
Expand Down Expand Up @@ -938,7 +938,7 @@ export class ExtractorConfig {
let reportFolder: string = tokenContext.projectFolder;
let reportTempFolder: string = tokenContext.projectFolder;
const reportConfigs: IExtractorConfigApiReport[] = [];
let tagsToReport: readonly string[] = defaultTagsToReport;
let tagsToReport: Record<`@${string}`, boolean> = {};
if (apiReportEnabled) {
// Undefined case checked above where we assign `apiReportEnabled`
const apiReportConfig: IConfigApiReport = configObject.apiReport!;
Expand Down Expand Up @@ -1008,7 +1008,10 @@ export class ExtractorConfig {
}

if (apiReportConfig.tagsToReport !== undefined) {
tagsToReport = apiReportConfig.tagsToReport;
tagsToReport = {
...defaultTagsToReport,
...apiReportConfig.tagsToReport
};
}
}

Expand Down
8 changes: 3 additions & 5 deletions apps/api-extractor/src/api/IConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,16 @@ export interface IConfigApiReport {
* items whose documentation contains them.
*
* @remarks
* Tag names must begin with \"@\".
* Tag names must begin with `@`.
*
* This list may include standard TSDoc tags as well as custom ones.
* TODO: document requirements around custom tags.
*
* Tags will appear in the order they are specified in this list.
*
* Note that an item's release tag will always reported; this behavior cannot be overridden.
*
* @defaultValue `["@sealed", "\@virtual", "@override", "@eventProperty", "@deprecated"]`
* @defaultValue `@sealed`, `@virtual`, `@override`, `@eventProperty`, `deprecated`
*/
tagsToReport?: string[];
tagsToReport?: Readonly<Record<`@${string}`, boolean>>;
}

/**
Expand Down
76 changes: 39 additions & 37 deletions apps/api-extractor/src/generators/ApiReportGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,43 +566,45 @@ export class ApiReportGenerator {
}
}

// 2. Enumerate configured tags in the order they were specified
for (const tag of collector.extractorConfig.tagsToReport) {
// Note that we check some tags specially.
switch (tag) {
case '@sealed':
if (apiItemMetadata.isSealed) {
footerParts.push(tag);
}
break;
case '@virtual':
if (apiItemMetadata.isVirtual) {
footerParts.push(tag);
}
break;
case '@override':
if (apiItemMetadata.isOverride) {
footerParts.push(tag);
}
break;
case '@eventProperty':
if (apiItemMetadata.isEventProperty) {
footerParts.push(tag);
}
break;
case '@deprecated':
if (apiItemMetadata.tsdocComment?.deprecatedBlock) {
footerParts.push(tag);
}
break;
default:
// If the tag was not handled specially, check if it is present in the metadata.
if (apiItemMetadata.tsdocComment?.customBlocks.some((block) => block.blockTag.tagName === tag)) {
footerParts.push(tag);
} else if (apiItemMetadata.tsdocComment?.modifierTagSet.hasTagName(tag)) {
footerParts.push(tag);
}
break;
// 2. Enumerate configured tags, reporting standard system tags first and then other configured tags.
// Note that the ordering we handle the standard tags is important for backwards compatibility.
// Also note that we had special mechanisms for checking whether or not an item is documented with these tags,
// so they are checked specially.
const {
'@sealed': reportSealedTag,
'@virtual': reportVirtualTag,
'@override': reportOverrideTag,
'@eventProperty': reportEventPropertyTag,
'@deprecated': reportDeprecatedTag,
...otherTagsToReport
} = collector.extractorConfig.tagsToReport;

// 2.a Check for standard tags and report those that are both configured and present in the metadata.
if (reportSealedTag && apiItemMetadata.isSealed) {
footerParts.push('@sealed');
}
if (reportVirtualTag && apiItemMetadata.isVirtual) {
footerParts.push('@virtual');
}
if (reportOverrideTag && apiItemMetadata.isOverride) {
footerParts.push('@override');
}
if (reportEventPropertyTag && apiItemMetadata.isEventProperty) {
footerParts.push('@eventProperty');
}
if (reportDeprecatedTag && apiItemMetadata.tsdocComment?.deprecatedBlock) {
footerParts.push('@deprecated');
}

// 2.b Check for other configured tags and report those that are present in the tsdoc metadata.
for (const [tag, reportTag] of Object.entries(otherTagsToReport)) {
if (reportTag) {
// If the tag was not handled specially, check if it is present in the metadata.
if (apiItemMetadata.tsdocComment?.customBlocks.some((block) => block.blockTag.tagName === tag)) {
footerParts.push(tag);
} else if (apiItemMetadata.tsdocComment?.modifierTagSet.hasTagName(tag)) {
footerParts.push(tag);
}
}
}

Expand Down
11 changes: 7 additions & 4 deletions apps/api-extractor/src/schemas/api-extractor.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,13 @@

"tagsToReport": {
"description": "Specifies a list of TSDoc tags that should be reported in the API report file for items whose documentation contains them. This can be used to include standard TSDoc tags or custom ones. Specified tag names must begin with \"@\". By default, the following tags are reported: [@sealed, @virtual, @override, @eventProperty, @deprecated]. Tags will appear in the order they are specified in this list. Note that an item's release tag will always reported; this behavior cannot be overridden.",
"type": "array",
"items": {
"type": "string"
}
"type": "object",
"patternProperties": {
"^@[^\\s]*$": {
"type": "boolean"
}
},
"additionalProperties": false
}
},
"required": ["enabled"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,13 @@ export class DocBaseClass {
export class DocClass1 extends DocBaseClass implements IDocInterface1, IDocInterface2 {
// @internal
constructor(name: string);
// @deprecated (undocumented)
// (undocumented)
deprecatedExample(): void;
exampleFunction(a: string, b: string): string;
exampleFunction(x: number): number;
genericWithConstraintAndDefault<T extends Constraint = DefaultType>(x: T): void;
interestingEdgeCases(): void;
// @eventProperty
malformedEvent: SystemEvent;
// @eventProperty
readonly modifiedEvent: SystemEvent;
protected static readonly multipleModifiersProperty: boolean;
optionalParamFunction(x?: number): void;
Expand Down Expand Up @@ -117,7 +115,7 @@ export interface IDocInterface1 {

// @public (undocumented)
export interface IDocInterface2 extends IDocInterface1 {
// @deprecated (undocumented)
// (undocumented)
deprecatedExample(): void;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@

"apiReport": {
"enabled": true,
"tagsToReport": ["@customBlockTag", "@customModifierTag"]
"tagsToReport": {
// Disable reporting of `@virtual`, which is reported by default
"@virtual": false,
// Enable reporting of our custom TSDoc tags.
"@customBlockTag": true,
"@customModifierTag": true
}
},

"docModel": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export declare class Lib3Class {
/**
* I am a documented property!
* @customBlockTag My docs include a custom block tag!
* @virtual @override
*/
prop: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export { Lib1Class }

// @public @customModifierTag (undocumented)
export class Lib3Class {
// @customBlockTag
// @override @customBlockTag
prop: boolean;
}

Expand Down
1 change: 1 addition & 0 deletions build-tests/api-extractor-lib3-test/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class Lib3Class {
/**
* I am a documented property!
* @customBlockTag My docs include a custom block tag!
* @virtual @override
*/
prop: boolean;
}
4 changes: 2 additions & 2 deletions common/reviews/api/api-extractor.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class ExtractorConfig {
readonly reportTempFolder: string;
readonly rollupEnabled: boolean;
readonly skipLibCheck: boolean;
readonly tagsToReport: readonly string[];
readonly tagsToReport: Readonly<Record<`@${string}`, boolean>>;
readonly testMode: boolean;
static tryLoadForFolder(options: IExtractorConfigLoadForFolderOptions): IExtractorConfigPrepareOptions | undefined;
readonly tsconfigFilePath: string;
Expand Down Expand Up @@ -187,7 +187,7 @@ export interface IConfigApiReport {
reportFolder?: string;
reportTempFolder?: string;
reportVariants?: ApiReportVariant[];
tagsToReport?: string[];
tagsToReport?: Readonly<Record<`@${string}`, boolean>>;
}

// @public
Expand Down

0 comments on commit 6dab01c

Please sign in to comment.