private _calculateApiItemMetadata()

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;
    }
  }