in apps/api-extractor/src/collector/Collector.ts [667:828]
private _calculateApiItemMetadata(astDeclaration: AstDeclaration): void {
const declarationMetadata: InternalDeclarationMetadata =
astDeclaration.declarationMetadata as InternalDeclarationMetadata;
if (declarationMetadata.isAncillary) {
if (astDeclaration.declaration.kind === ts.SyntaxKind.SetAccessor) {
if (declarationMetadata.tsdocParserContext) {
this.messageRouter.addAnalyzerIssue(
ExtractorMessageId.SetterWithDocs,
`The doc comment for the property "${astDeclaration.astSymbol.localName}"` +
` must appear on the getter, not the setter.`,
astDeclaration
);
}
}
// We never calculate ApiItemMetadata for an ancillary declaration; instead, it is assigned when
// the main declaration is processed.
return;
}
const options: IApiItemMetadataOptions = {
declaredReleaseTag: ReleaseTag.None,
effectiveReleaseTag: ReleaseTag.None,
isEventProperty: false,
isOverride: false,
isSealed: false,
isVirtual: false,
isPreapproved: false,
releaseTagSameAsParent: false
};
const parserContext: tsdoc.ParserContext | undefined = declarationMetadata.tsdocParserContext;
if (parserContext) {
const modifierTagSet: tsdoc.StandardModifierTagSet = parserContext.docComment.modifierTagSet;
let declaredReleaseTag: ReleaseTag = ReleaseTag.None;
let extraReleaseTags: boolean = false;
if (modifierTagSet.isPublic()) {
declaredReleaseTag = ReleaseTag.Public;
}
if (modifierTagSet.isBeta()) {
if (declaredReleaseTag !== ReleaseTag.None) {
extraReleaseTags = true;
} else {
declaredReleaseTag = ReleaseTag.Beta;
}
}
if (modifierTagSet.isAlpha()) {
if (declaredReleaseTag !== ReleaseTag.None) {
extraReleaseTags = true;
} else {
declaredReleaseTag = ReleaseTag.Alpha;
}
}
if (modifierTagSet.isInternal()) {
if (declaredReleaseTag !== ReleaseTag.None) {
extraReleaseTags = true;
} else {
declaredReleaseTag = ReleaseTag.Internal;
}
}
if (extraReleaseTags) {
if (!astDeclaration.astSymbol.isExternal) {
// for now, don't report errors for external code
this.messageRouter.addAnalyzerIssue(
ExtractorMessageId.ExtraReleaseTag,
'The doc comment should not contain more than one release tag',
astDeclaration
);
}
}
options.declaredReleaseTag = declaredReleaseTag;
options.isEventProperty = modifierTagSet.isEventProperty();
options.isOverride = modifierTagSet.isOverride();
options.isSealed = modifierTagSet.isSealed();
options.isVirtual = modifierTagSet.isVirtual();
const preapprovedTag: tsdoc.TSDocTagDefinition | void =
this.extractorConfig.tsdocConfiguration.tryGetTagDefinition('@preapproved');
if (preapprovedTag && modifierTagSet.hasTag(preapprovedTag)) {
// This feature only makes sense for potentially big declarations.
switch (astDeclaration.declaration.kind) {
case ts.SyntaxKind.ClassDeclaration:
case ts.SyntaxKind.EnumDeclaration:
case ts.SyntaxKind.InterfaceDeclaration:
case ts.SyntaxKind.ModuleDeclaration:
if (declaredReleaseTag === ReleaseTag.Internal) {
options.isPreapproved = true;
} else {
this.messageRouter.addAnalyzerIssue(
ExtractorMessageId.PreapprovedBadReleaseTag,
`The @preapproved tag cannot be applied to "${astDeclaration.astSymbol.localName}"` +
` without an @internal release tag`,
astDeclaration
);
}
break;
default:
this.messageRouter.addAnalyzerIssue(
ExtractorMessageId.PreapprovedUnsupportedType,
`The @preapproved tag cannot be applied to "${astDeclaration.astSymbol.localName}"` +
` because it is not a supported declaration type`,
astDeclaration
);
break;
}
}
}
// This needs to be set regardless of whether or not a parserContext exists
if (astDeclaration.parent) {
const parentApiItemMetadata: ApiItemMetadata = this.fetchApiItemMetadata(astDeclaration.parent);
options.effectiveReleaseTag =
options.declaredReleaseTag === ReleaseTag.None
? parentApiItemMetadata.effectiveReleaseTag
: options.declaredReleaseTag;
options.releaseTagSameAsParent =
parentApiItemMetadata.effectiveReleaseTag === options.effectiveReleaseTag;
} else {
options.effectiveReleaseTag = options.declaredReleaseTag;
}
if (options.effectiveReleaseTag === ReleaseTag.None) {
if (!astDeclaration.astSymbol.isExternal) {
// for now, don't report errors for external code
// Don't report missing release tags for forgotten exports
const astSymbol: AstSymbol = astDeclaration.astSymbol;
const entity: CollectorEntity | undefined = this._entitiesByAstEntity.get(astSymbol.rootAstSymbol);
if (entity && entity.consumable) {
// We also don't report errors for the default export of an entry point, since its doc comment
// isn't easy to obtain from the .d.ts file
if (astSymbol.rootAstSymbol.localName !== '_default') {
this.messageRouter.addAnalyzerIssue(
ExtractorMessageId.MissingReleaseTag,
`"${entity.astEntity.localName}" is exported by the package, but it is missing ` +
`a release tag (@alpha, @beta, @public, or @internal)`,
astSymbol
);
}
}
}
options.effectiveReleaseTag = ReleaseTag.Public;
}
const apiItemMetadata: ApiItemMetadata = new ApiItemMetadata(options);
if (parserContext) {
apiItemMetadata.tsdocComment = parserContext.docComment;
}
astDeclaration.apiItemMetadata = apiItemMetadata;
// Lastly, share the result with any ancillary declarations
for (const ancillaryDeclaration of declarationMetadata.ancillaryDeclarations) {
ancillaryDeclaration.apiItemMetadata = apiItemMetadata;
}
}