mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-12 09:33:32 +01:00
build: package api-extractor and -model (#9920)
* fix(ExceptText): don't display import("d..-types/v10"). in return type
* Squashed 'packages/api-extractor-model/' content from commit 39ecb196c
git-subtree-dir: packages/api-extractor-model
git-subtree-split: 39ecb196ca210bdf84ba6c9cadb1bb93571849d7
* Squashed 'packages/api-extractor/' content from commit 341ad6c51
git-subtree-dir: packages/api-extractor
git-subtree-split: 341ad6c51b01656d4f73b74ad4bdb3095f9262c4
* feat(api-extractor): add api-extractor and -model
* fix: package.json docs script
* fix(SourcLink): use <> instead of function syntax
* fix: make packages private
* fix: rest params showing in docs, added labels
* fix: missed two files
* fix: cpy-cli & pnpm-lock
* fix: increase icon size
* fix: icon size again
This commit is contained in:
68
packages/api-extractor-model/src/aedoc/AedocDefinitions.ts
Normal file
68
packages/api-extractor-model/src/aedoc/AedocDefinitions.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { TSDocConfiguration, TSDocTagDefinition, TSDocTagSyntaxKind, StandardTags } from '@microsoft/tsdoc';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @deprecated - tsdoc configuration is now constructed from tsdoc.json files associated with each package.
|
||||
*/
|
||||
export class AedocDefinitions {
|
||||
public static readonly betaDocumentation: TSDocTagDefinition = new TSDocTagDefinition({
|
||||
tagName: '@betaDocumentation',
|
||||
syntaxKind: TSDocTagSyntaxKind.ModifierTag,
|
||||
});
|
||||
|
||||
public static readonly internalRemarks: TSDocTagDefinition = new TSDocTagDefinition({
|
||||
tagName: '@internalRemarks',
|
||||
syntaxKind: TSDocTagSyntaxKind.BlockTag,
|
||||
});
|
||||
|
||||
public static readonly preapprovedTag: TSDocTagDefinition = new TSDocTagDefinition({
|
||||
tagName: '@preapproved',
|
||||
syntaxKind: TSDocTagSyntaxKind.ModifierTag,
|
||||
});
|
||||
|
||||
public static get tsdocConfiguration(): TSDocConfiguration {
|
||||
if (!AedocDefinitions._tsdocConfiguration) {
|
||||
const configuration: TSDocConfiguration = new TSDocConfiguration();
|
||||
configuration.addTagDefinitions(
|
||||
[AedocDefinitions.betaDocumentation, AedocDefinitions.internalRemarks, AedocDefinitions.preapprovedTag],
|
||||
true,
|
||||
);
|
||||
|
||||
configuration.setSupportForTags(
|
||||
[
|
||||
StandardTags.alpha,
|
||||
StandardTags.beta,
|
||||
StandardTags.decorator,
|
||||
StandardTags.defaultValue,
|
||||
StandardTags.deprecated,
|
||||
StandardTags.eventProperty,
|
||||
StandardTags.example,
|
||||
StandardTags.inheritDoc,
|
||||
StandardTags.internal,
|
||||
StandardTags.link,
|
||||
StandardTags.override,
|
||||
StandardTags.packageDocumentation,
|
||||
StandardTags.param,
|
||||
StandardTags.privateRemarks,
|
||||
StandardTags.public,
|
||||
StandardTags.readonly,
|
||||
StandardTags.remarks,
|
||||
StandardTags.returns,
|
||||
StandardTags.sealed,
|
||||
StandardTags.throws,
|
||||
StandardTags.virtual,
|
||||
],
|
||||
true,
|
||||
);
|
||||
|
||||
AedocDefinitions._tsdocConfiguration = configuration;
|
||||
}
|
||||
|
||||
return AedocDefinitions._tsdocConfiguration;
|
||||
}
|
||||
|
||||
private static _tsdocConfiguration: TSDocConfiguration | undefined;
|
||||
}
|
||||
88
packages/api-extractor-model/src/aedoc/ReleaseTag.ts
Normal file
88
packages/api-extractor-model/src/aedoc/ReleaseTag.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
/**
|
||||
* A "release tag" is a custom TSDoc tag that is applied to an API to communicate the level of support
|
||||
* provided for third-party developers.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The four release tags are: `@internal`, `@alpha`, `@beta`, and `@public`. They are applied to API items such
|
||||
* as classes, member functions, enums, etc. The release tag applies recursively to members of a container
|
||||
* (e.g. class or interface). For example, if a class is marked as `@beta`, then all of its members automatically
|
||||
* have this status; you DON'T need add the `@beta` tag to each member function. However, you could add
|
||||
* `@internal` to a member function to give it a different release status.
|
||||
* @public
|
||||
*/
|
||||
export enum ReleaseTag {
|
||||
/**
|
||||
* No release tag was specified in the AEDoc summary.
|
||||
*/
|
||||
None = 0,
|
||||
/**
|
||||
* Indicates that an API item is meant only for usage by other NPM packages from the same
|
||||
* maintainer. Third parties should never use "internal" APIs. (To emphasize this, their
|
||||
* names are prefixed by underscores.)
|
||||
*/
|
||||
Internal = 1,
|
||||
/**
|
||||
* Indicates that an API item is eventually intended to be public, but currently is in an
|
||||
* early stage of development. Third parties should not use "alpha" APIs.
|
||||
*/
|
||||
Alpha = 2,
|
||||
/**
|
||||
* Indicates that an API item has been released in an experimental state. Third parties are
|
||||
* encouraged to try it and provide feedback. However, a "beta" API should NOT be used
|
||||
* in production.
|
||||
*/
|
||||
Beta = 3,
|
||||
/**
|
||||
* Indicates that an API item has been officially released. It is part of the supported
|
||||
* contract (e.g. SemVer) for a package.
|
||||
*/
|
||||
Public = 4,
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper functions for working with the `ReleaseTag` enum.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
// export namespace ReleaseTag {
|
||||
/**
|
||||
* Returns the TSDoc tag name for a `ReleaseTag` value.
|
||||
*
|
||||
* @remarks
|
||||
* For example, `getTagName(ReleaseTag.Internal)` would return the string `@internal`.
|
||||
*/
|
||||
export function getTagName(releaseTag: ReleaseTag): string {
|
||||
switch (releaseTag) {
|
||||
case ReleaseTag.None:
|
||||
return '(none)';
|
||||
case ReleaseTag.Internal:
|
||||
return '@internal';
|
||||
case ReleaseTag.Alpha:
|
||||
return '@alpha';
|
||||
case ReleaseTag.Beta:
|
||||
return '@beta';
|
||||
case ReleaseTag.Public:
|
||||
return '@public';
|
||||
default:
|
||||
throw new Error('Unsupported release tag');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two `ReleaseTag` values. Their values must not be `ReleaseTag.None`.
|
||||
*
|
||||
* @returns 0 if `a` and `b` are equal, a positive number if `a` is more public than `b`,
|
||||
* and a negative number if `a` is less public than `b`.
|
||||
* @remarks
|
||||
* For example, `compareReleaseTag(ReleaseTag.Beta, ReleaseTag.Alpha)` will return a positive
|
||||
* number because beta is more public than alpha.
|
||||
*/
|
||||
export function compare(a: ReleaseTag, b: ReleaseTag): number {
|
||||
return a - b;
|
||||
}
|
||||
// }
|
||||
84
packages/api-extractor-model/src/index.ts
Normal file
84
packages/api-extractor-model/src/index.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
/**
|
||||
* Use this library to read and write *.api.json files as defined by the
|
||||
* {@link https://api-extractor.com/ | API Extractor} tool. These files are used to generate a documentation
|
||||
* website for your TypeScript package. The files store the API signatures and doc comments that were extracted
|
||||
* from your package.
|
||||
*
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export { AedocDefinitions } from './aedoc/AedocDefinitions.js';
|
||||
export { ReleaseTag, compare as releaseTagCompare, getTagName as releaseTagGetTagName } from './aedoc/ReleaseTag.js';
|
||||
|
||||
// items
|
||||
export { type IApiDeclaredItemOptions, ApiDeclaredItem } from './items/ApiDeclaredItem.js';
|
||||
export { type IApiDocumentedItemOptions, ApiDocumentedItem } from './items/ApiDocumentedItem.js';
|
||||
export { ApiItemKind, type IApiItemOptions, ApiItem, type IApiItemConstructor } from './items/ApiItem.js';
|
||||
export { type IApiPropertyItemOptions, ApiPropertyItem } from './items/ApiPropertyItem.js';
|
||||
|
||||
// mixins
|
||||
export {
|
||||
type IApiParameterListMixinOptions,
|
||||
type IApiParameterOptions,
|
||||
ApiParameterListMixin,
|
||||
} from './mixins/ApiParameterListMixin.js';
|
||||
export {
|
||||
type IApiTypeParameterOptions,
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
ApiTypeParameterListMixin,
|
||||
} from './mixins/ApiTypeParameterListMixin.js';
|
||||
export { type IApiAbstractMixinOptions, ApiAbstractMixin } from './mixins/ApiAbstractMixin.js';
|
||||
export { type IApiItemContainerMixinOptions, ApiItemContainerMixin } from './mixins/ApiItemContainerMixin.js';
|
||||
export { type IApiProtectedMixinOptions, ApiProtectedMixin } from './mixins/ApiProtectedMixin.js';
|
||||
export { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from './mixins/ApiReleaseTagMixin.js';
|
||||
export { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from './mixins/ApiReturnTypeMixin.js';
|
||||
export { type IApiStaticMixinOptions, ApiStaticMixin } from './mixins/ApiStaticMixin.js';
|
||||
export { type IApiNameMixinOptions, ApiNameMixin } from './mixins/ApiNameMixin.js';
|
||||
export { type IApiOptionalMixinOptions, ApiOptionalMixin } from './mixins/ApiOptionalMixin.js';
|
||||
export { type IApiReadonlyMixinOptions, ApiReadonlyMixin } from './mixins/ApiReadonlyMixin.js';
|
||||
export { type IApiInitializerMixinOptions, ApiInitializerMixin } from './mixins/ApiInitializerMixin.js';
|
||||
export { type IApiExportedMixinOptions, ApiExportedMixin } from './mixins/ApiExportedMixin.js';
|
||||
export {
|
||||
type IFindApiItemsResult,
|
||||
type IFindApiItemsMessage,
|
||||
FindApiItemsMessageId,
|
||||
} from './mixins/IFindApiItemsResult.js';
|
||||
|
||||
export {
|
||||
ExcerptTokenKind,
|
||||
type IExcerptTokenRange,
|
||||
type IExcerptToken,
|
||||
ExcerptToken,
|
||||
Excerpt,
|
||||
} from './mixins/Excerpt.js';
|
||||
export type { Constructor, PropertiesOf } from './mixins/Mixin.js';
|
||||
|
||||
// model
|
||||
export { type IApiCallSignatureOptions, ApiCallSignature } from './model/ApiCallSignature.js';
|
||||
export { type IApiClassOptions, ApiClass } from './model/ApiClass.js';
|
||||
export { type IApiConstructorOptions, ApiConstructor } from './model/ApiConstructor.js';
|
||||
export { type IApiConstructSignatureOptions, ApiConstructSignature } from './model/ApiConstructSignature.js';
|
||||
export { type IApiEntryPointOptions, ApiEntryPoint } from './model/ApiEntryPoint.js';
|
||||
export { type IApiEnumOptions, ApiEnum } from './model/ApiEnum.js';
|
||||
export { type IApiEnumMemberOptions, ApiEnumMember, EnumMemberOrder } from './model/ApiEnumMember.js';
|
||||
export { type IApiFunctionOptions, ApiFunction } from './model/ApiFunction.js';
|
||||
export { type IApiIndexSignatureOptions, ApiIndexSignature } from './model/ApiIndexSignature.js';
|
||||
export { type IApiInterfaceOptions, ApiInterface } from './model/ApiInterface.js';
|
||||
export { type IApiMethodOptions, ApiMethod } from './model/ApiMethod.js';
|
||||
export { type IApiMethodSignatureOptions, ApiMethodSignature } from './model/ApiMethodSignature.js';
|
||||
export { ApiModel } from './model/ApiModel.js';
|
||||
export { type IApiNamespaceOptions, ApiNamespace } from './model/ApiNamespace.js';
|
||||
export { type IApiPackageOptions, ApiPackage, type IApiPackageSaveOptions } from './model/ApiPackage.js';
|
||||
export { type IParameterOptions, Parameter } from './model/Parameter.js';
|
||||
export { type IApiPropertyOptions, ApiProperty } from './model/ApiProperty.js';
|
||||
export { type IApiPropertySignatureOptions, ApiPropertySignature } from './model/ApiPropertySignature.js';
|
||||
export { type IApiTypeAliasOptions, ApiTypeAlias } from './model/ApiTypeAlias.js';
|
||||
export { type ITypeParameterOptions, TypeParameter } from './model/TypeParameter.js';
|
||||
export { type IApiVariableOptions, ApiVariable } from './model/ApiVariable.js';
|
||||
export type { IResolveDeclarationReferenceResult } from './model/ModelReferenceResolver.js';
|
||||
export { HeritageType } from './model/HeritageType.js';
|
||||
export { type ISourceLocationOptions, SourceLocation } from './model/SourceLocation.js';
|
||||
export { Navigation, Meaning } from './items/ApiItem.js';
|
||||
225
packages/api-extractor-model/src/items/ApiDeclaredItem.ts
Normal file
225
packages/api-extractor-model/src/items/ApiDeclaredItem.ts
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { Excerpt, ExcerptToken, type IExcerptTokenRange, type IExcerptToken } from '../mixins/Excerpt.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import { SourceLocation } from '../model/SourceLocation.js';
|
||||
import { ApiDocumentedItem, type IApiDocumentedItemJson, type IApiDocumentedItemOptions } from './ApiDocumentedItem.js';
|
||||
import type { ApiItem } from './ApiItem.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiDeclaredItem}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiDeclaredItemOptions extends IApiDocumentedItemOptions {
|
||||
excerptTokens: IExcerptToken[];
|
||||
fileColumn?: number | undefined;
|
||||
fileLine?: number | undefined;
|
||||
fileUrlPath?: string | undefined;
|
||||
}
|
||||
|
||||
export interface IApiDeclaredItemJson extends IApiDocumentedItemJson {
|
||||
excerptTokens: IExcerptToken[];
|
||||
fileColumn?: number;
|
||||
fileLine?: number;
|
||||
fileUrlPath?: string | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base class for API items that have an associated source code excerpt containing a TypeScript declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* Most `ApiItem` subclasses have declarations and thus extend `ApiDeclaredItem`. Counterexamples include
|
||||
* `ApiModel` and `ApiPackage`, which do not have any corresponding TypeScript source code.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export class ApiDeclaredItem extends ApiDocumentedItem {
|
||||
private readonly _excerptTokens: ExcerptToken[];
|
||||
|
||||
private readonly _excerpt: Excerpt;
|
||||
|
||||
private readonly _fileUrlPath?: string | undefined;
|
||||
|
||||
private readonly _fileLine?: number | undefined;
|
||||
|
||||
private readonly _fileColumn?: number | undefined;
|
||||
|
||||
private _sourceLocation?: SourceLocation;
|
||||
|
||||
public constructor(options: IApiDeclaredItemOptions) {
|
||||
super(options);
|
||||
|
||||
this._excerptTokens = options.excerptTokens.map((token) => {
|
||||
const canonicalReference: DeclarationReference | undefined =
|
||||
token.canonicalReference === undefined ? undefined : DeclarationReference.parse(token.canonicalReference);
|
||||
return new ExcerptToken(token.kind, token.text, canonicalReference);
|
||||
});
|
||||
this._excerpt = new Excerpt(this.excerptTokens, { startIndex: 0, endIndex: this.excerptTokens.length });
|
||||
this._fileUrlPath = options.fileUrlPath;
|
||||
this._fileLine = options.fileLine;
|
||||
this._fileColumn = options.fileColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiDeclaredItemOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiDeclaredItemJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.excerptTokens = jsonObject.excerptTokens;
|
||||
options.fileUrlPath = jsonObject.fileUrlPath;
|
||||
options.fileLine = jsonObject.fileLine;
|
||||
options.fileColumn = jsonObject.fileColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source code excerpt where the API item is declared.
|
||||
*/
|
||||
public get excerpt(): Excerpt {
|
||||
return this._excerpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* The individual source code tokens that comprise the main excerpt.
|
||||
*/
|
||||
public get excerptTokens(): readonly ExcerptToken[] {
|
||||
return this._excerptTokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* The file URL path relative to the `projectFolder` and `projectFolderURL` fields
|
||||
* as defined in the `api-extractor.json` config. Is `undefined` if the path is
|
||||
* the same as the parent API item's.
|
||||
*/
|
||||
public get fileUrlPath(): string | undefined {
|
||||
return this._fileUrlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* The line in the `fileUrlPath` where the API item is declared.
|
||||
*/
|
||||
public get fileLine(): number | undefined {
|
||||
return this._fileLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* The column in the `fileUrlPath` where the API item is declared.
|
||||
*/
|
||||
public get fileColumn(): number | undefined {
|
||||
return this._fileColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source location where the API item is declared.
|
||||
*/
|
||||
public get sourceLocation(): SourceLocation {
|
||||
if (!this._sourceLocation) {
|
||||
this._sourceLocation = this._buildSourceLocation();
|
||||
}
|
||||
|
||||
return this._sourceLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the API item has certain important modifier tags such as `@sealed`, `@virtual`, or `@override`,
|
||||
* this prepends them as a doc comment above the excerpt.
|
||||
*/
|
||||
public getExcerptWithModifiers(): string {
|
||||
const excerpt: string = this.excerpt.text;
|
||||
const modifierTags: string[] = [];
|
||||
|
||||
if (excerpt.length > 0 && this instanceof ApiDocumentedItem) {
|
||||
if (this.tsdocComment) {
|
||||
if (this.tsdocComment.modifierTagSet.isSealed()) {
|
||||
modifierTags.push('@sealed');
|
||||
}
|
||||
|
||||
if (this.tsdocComment.modifierTagSet.isVirtual()) {
|
||||
modifierTags.push('@virtual');
|
||||
}
|
||||
|
||||
if (this.tsdocComment.modifierTagSet.isOverride()) {
|
||||
modifierTags.push('@override');
|
||||
}
|
||||
}
|
||||
|
||||
if (modifierTags.length > 0) {
|
||||
return '/** ' + modifierTags.join(' ') + ' */\n' + excerpt;
|
||||
}
|
||||
}
|
||||
|
||||
return excerpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiDeclaredItemJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
jsonObject.excerptTokens = this.excerptTokens.map((x) => {
|
||||
const excerptToken: IExcerptToken = { kind: x.kind, text: x.text };
|
||||
if (x.canonicalReference !== undefined) {
|
||||
excerptToken.canonicalReference = x.canonicalReference.toString();
|
||||
}
|
||||
|
||||
return excerptToken;
|
||||
});
|
||||
|
||||
// Only serialize this API item's file URL path if it exists and it's different from its parent's
|
||||
// (a little optimization to keep the doc model succinct).
|
||||
if (
|
||||
this.fileUrlPath &&
|
||||
(!(this.parent instanceof ApiDeclaredItem) || this.fileUrlPath !== this.parent.fileUrlPath)
|
||||
) {
|
||||
jsonObject.fileUrlPath = this.fileUrlPath;
|
||||
}
|
||||
|
||||
if (this.fileLine) {
|
||||
jsonObject.fileLine = this.fileLine;
|
||||
}
|
||||
|
||||
if (this.fileColumn) {
|
||||
jsonObject.fileColumn = this.fileColumn;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new {@link Excerpt} corresponding to the provided token range.
|
||||
*/
|
||||
public buildExcerpt(tokenRange: IExcerptTokenRange): Excerpt {
|
||||
return new Excerpt(this.excerptTokens, tokenRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the cached object used by the `sourceLocation` property.
|
||||
*/
|
||||
private _buildSourceLocation(): SourceLocation {
|
||||
const projectFolderUrl: string | undefined = this.getAssociatedPackage()?.projectFolderUrl;
|
||||
|
||||
let fileUrlPath: string | undefined;
|
||||
for (let current: ApiItem | undefined = this; current !== undefined; current = current.parent) {
|
||||
if (current instanceof ApiDeclaredItem && current.fileUrlPath) {
|
||||
fileUrlPath = current.fileUrlPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new SourceLocation({
|
||||
projectFolderUrl,
|
||||
fileUrlPath,
|
||||
sourceFileColumn: this.fileColumn,
|
||||
sourceFileLine: this.fileLine,
|
||||
});
|
||||
}
|
||||
}
|
||||
78
packages/api-extractor-model/src/items/ApiDocumentedItem.ts
Normal file
78
packages/api-extractor-model/src/items/ApiDocumentedItem.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import * as tsdoc from '@microsoft/tsdoc';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import { ApiItem, type IApiItemOptions, type IApiItemJson } from './ApiItem.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiDocumentedItem}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiDocumentedItemOptions extends IApiItemOptions {
|
||||
docComment: tsdoc.DocComment | undefined;
|
||||
}
|
||||
|
||||
export interface IApiDocumentedItemJson extends IApiItemJson {
|
||||
docComment: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract base class for API declarations that can have an associated TSDoc comment.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
* @public
|
||||
*/
|
||||
export class ApiDocumentedItem extends ApiItem {
|
||||
private readonly _tsdocComment: tsdoc.DocComment | undefined;
|
||||
|
||||
public constructor(options: IApiDocumentedItemOptions) {
|
||||
super(options);
|
||||
this._tsdocComment = options.docComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiDocumentedItemOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiItemJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
const documentedJson: IApiDocumentedItemJson = jsonObject as IApiDocumentedItemJson;
|
||||
|
||||
if (documentedJson.docComment) {
|
||||
const tsdocParser: tsdoc.TSDocParser = new tsdoc.TSDocParser(context.tsdocConfiguration);
|
||||
|
||||
// NOTE: For now, we ignore TSDoc errors found in a serialized .api.json file.
|
||||
// Normally these errors would have already been reported by API Extractor during analysis.
|
||||
// However, they could also arise if the JSON file was edited manually, or if the file was saved
|
||||
// using a different release of the software that used an incompatible syntax.
|
||||
const parserContext: tsdoc.ParserContext = tsdocParser.parseString(documentedJson.docComment);
|
||||
|
||||
options.docComment = parserContext.docComment;
|
||||
}
|
||||
}
|
||||
|
||||
public get tsdocComment(): tsdoc.DocComment | undefined {
|
||||
return this._tsdocComment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiDocumentedItemJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
if (this.tsdocComment === undefined) {
|
||||
jsonObject.docComment = '';
|
||||
} else {
|
||||
jsonObject.docComment = this.tsdocComment.emitAsTsdoc();
|
||||
}
|
||||
}
|
||||
}
|
||||
367
packages/api-extractor-model/src/items/ApiItem.ts
Normal file
367
packages/api-extractor-model/src/items/ApiItem.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { InternalError } from '@rushstack/node-core-library';
|
||||
import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import type { Constructor, PropertiesOf } from '../mixins/Mixin.js';
|
||||
import type { ApiModel } from '../model/ApiModel.js';
|
||||
import type { ApiPackage } from '../model/ApiPackage.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* The type returned by the {@link ApiItem.kind} property, which can be used to easily distinguish subclasses of
|
||||
* {@link ApiItem}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum ApiItemKind {
|
||||
CallSignature = 'CallSignature',
|
||||
Class = 'Class',
|
||||
ConstructSignature = 'ConstructSignature',
|
||||
Constructor = 'Constructor',
|
||||
EntryPoint = 'EntryPoint',
|
||||
Enum = 'Enum',
|
||||
EnumMember = 'EnumMember',
|
||||
Function = 'Function',
|
||||
IndexSignature = 'IndexSignature',
|
||||
Interface = 'Interface',
|
||||
Method = 'Method',
|
||||
MethodSignature = 'MethodSignature',
|
||||
Model = 'Model',
|
||||
Namespace = 'Namespace',
|
||||
None = 'None',
|
||||
Package = 'Package',
|
||||
Property = 'Property',
|
||||
PropertySignature = 'PropertySignature',
|
||||
TypeAlias = 'TypeAlias',
|
||||
Variable = 'Variable',
|
||||
}
|
||||
/**
|
||||
* Indicates the symbol table from which to resolve the next symbol component.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
export enum Navigation {
|
||||
Exports = '.',
|
||||
Locals = '~',
|
||||
Members = '#',
|
||||
}
|
||||
/**
|
||||
* @beta
|
||||
*/
|
||||
export enum Meaning {
|
||||
CallSignature = 'call',
|
||||
Class = 'class',
|
||||
ComplexType = 'complex',
|
||||
ConstructSignature = 'new',
|
||||
Constructor = 'constructor',
|
||||
Enum = 'enum',
|
||||
Event = 'event',
|
||||
Function = 'function',
|
||||
IndexSignature = 'index',
|
||||
Interface = 'interface',
|
||||
Member = 'member',
|
||||
Namespace = 'namespace',
|
||||
TypeAlias = 'type',
|
||||
Variable = 'var',
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiItem}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface IApiItemOptions {}
|
||||
|
||||
export interface IApiItemJson {
|
||||
canonicalReference: string;
|
||||
kind: ApiItemKind;
|
||||
}
|
||||
|
||||
// PRIVATE - Allows ApiItemContainerMixin to assign the parent.
|
||||
//
|
||||
export const apiItem_onParentChanged: unique symbol = Symbol('ApiItem._onAddToContainer');
|
||||
|
||||
/**
|
||||
* The abstract base class for all members of an `ApiModel` object.
|
||||
*
|
||||
* @remarks
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
* @public
|
||||
*/
|
||||
export class ApiItem {
|
||||
private _canonicalReference: DeclarationReference | undefined;
|
||||
|
||||
private _parent: ApiItem | undefined;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
||||
public constructor(_options: IApiItemOptions) {
|
||||
// ("options" is not used here, but part of the inheritance pattern)
|
||||
}
|
||||
|
||||
public static deserialize(jsonObject: IApiItemJson, context: DeserializerContext): ApiItem {
|
||||
// The Deserializer class is coupled with a ton of other classes, so we delay loading it
|
||||
// to avoid ES5 circular imports.
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-imports, @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires
|
||||
const deserializerModule: typeof import('../model/Deserializer') = require('../model/Deserializer');
|
||||
return deserializerModule.Deserializer.deserialize(context, jsonObject);
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
*/
|
||||
public static onDeserializeInto(
|
||||
_options: Partial<IApiItemOptions>,
|
||||
_context: DeserializerContext,
|
||||
_jsonObject: IApiItemJson,
|
||||
): void {
|
||||
// (implemented by subclasses)
|
||||
}
|
||||
|
||||
/**
|
||||
* @virtual
|
||||
*/
|
||||
public serializeInto(jsonObject: Partial<IApiItemJson>): void {
|
||||
jsonObject.kind = this.kind;
|
||||
jsonObject.canonicalReference = this.canonicalReference.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies the subclass of the `ApiItem` base class.
|
||||
*
|
||||
* @virtual
|
||||
*/
|
||||
public get kind(): ApiItemKind {
|
||||
throw new Error('ApiItem.kind was not implemented by the child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Warning: This API is used internally by API extractor but is not yet ready for general usage.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Returns a `DeclarationReference` object using the experimental new declaration reference notation.
|
||||
* @beta
|
||||
*/
|
||||
public get canonicalReference(): DeclarationReference {
|
||||
if (!this._canonicalReference) {
|
||||
try {
|
||||
this._canonicalReference = this.buildCanonicalReference();
|
||||
} catch (error) {
|
||||
const name: string = this.getScopedNameWithinPackage() || this.displayName;
|
||||
throw new InternalError(`Error building canonical reference for ${name}:\n` + (error as Error).message);
|
||||
}
|
||||
}
|
||||
|
||||
return this._canonicalReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string key that can be used to efficiently retrieve an `ApiItem` from an `ApiItemContainerMixin`.
|
||||
* The key is unique within the container. Its format is undocumented and may change at any time.
|
||||
*
|
||||
* @remarks
|
||||
* Use the `getContainerKey()` static member to construct the key. Each subclass has a different implementation
|
||||
* of this function, according to the aspects that are important for identifying it.
|
||||
* @virtual
|
||||
*/
|
||||
public get containerKey(): string {
|
||||
throw new InternalError('ApiItem.containerKey was not implemented by the child class');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a name for this object that can be used in diagnostic messages, for example.
|
||||
*
|
||||
* @remarks
|
||||
* For an object that inherits ApiNameMixin, this will return the declared name (e.g. the name of a TypeScript
|
||||
* function). Otherwise, it will return a string such as "(call signature)" or "(model)".
|
||||
* @virtual
|
||||
*/
|
||||
public get displayName(): string {
|
||||
switch (this.kind) {
|
||||
case ApiItemKind.CallSignature:
|
||||
return '(call)';
|
||||
case ApiItemKind.Constructor:
|
||||
return '(constructor)';
|
||||
case ApiItemKind.ConstructSignature:
|
||||
return '(new)';
|
||||
case ApiItemKind.IndexSignature:
|
||||
return '(indexer)';
|
||||
case ApiItemKind.Model:
|
||||
return '(model)';
|
||||
default:
|
||||
return '(???)'; // All other types should inherit ApiNameMixin which will override this property
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this item was added to a ApiItemContainerMixin item, then this returns the container item.
|
||||
* If this is an Parameter that was added to a method or function, then this returns the function item.
|
||||
* Otherwise, it returns undefined.
|
||||
*
|
||||
* @virtual
|
||||
*/
|
||||
public get parent(): ApiItem | undefined {
|
||||
return this._parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* This property supports a visitor pattern for walking the tree.
|
||||
* For items with ApiItemContainerMixin, it returns the contained items, sorted alphabetically.
|
||||
* Otherwise it returns an empty array.
|
||||
*
|
||||
* @virtual
|
||||
*/
|
||||
public get members(): readonly ApiItem[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* If this item has a name (i.e. extends `ApiNameMixin`), then return all items that have the same parent
|
||||
* and the same name. Otherwise, return all items that have the same parent and the same `ApiItemKind`.
|
||||
*
|
||||
* @remarks
|
||||
* Examples: For a function, this would return all overloads for the function. For a constructor, this would
|
||||
* return all overloads for the constructor. For a merged declaration (e.g. a `namespace` and `enum` with the
|
||||
* same name), this would return both declarations. If this item does not have a parent, or if it is the only
|
||||
* item of its name/kind, then the result is an array containing only this item.
|
||||
*/
|
||||
public getMergedSiblings(): readonly ApiItem[] {
|
||||
const parent: ApiItem | undefined = this._parent;
|
||||
if (parent && ApiItemContainerMixin.isBaseClassOf(parent)) {
|
||||
return parent._getMergedSiblingsForMember(this);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the chain of ancestors, starting from the root of the tree, and ending with the this item.
|
||||
*/
|
||||
public getHierarchy(): readonly ApiItem[] {
|
||||
const hierarchy: ApiItem[] = [];
|
||||
for (let current: ApiItem | undefined = this; current !== undefined; current = current.parent) {
|
||||
hierarchy.push(current);
|
||||
}
|
||||
|
||||
hierarchy.reverse();
|
||||
return hierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns a scoped name such as `"Namespace1.Namespace2.MyClass.myMember()"`. It does not include the
|
||||
* package name or entry point.
|
||||
*
|
||||
* @remarks
|
||||
* If called on an ApiEntrypoint, ApiPackage, or ApiModel item, the result is an empty string.
|
||||
*/
|
||||
public getScopedNameWithinPackage(): string {
|
||||
const reversedParts: string[] = [];
|
||||
|
||||
for (let current: ApiItem | undefined = this; current !== undefined; current = current.parent) {
|
||||
if (
|
||||
current.kind === ApiItemKind.Model ||
|
||||
current.kind === ApiItemKind.Package ||
|
||||
current.kind === ApiItemKind.EntryPoint
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (reversedParts.length === 0) {
|
||||
switch (current.kind) {
|
||||
case ApiItemKind.CallSignature:
|
||||
case ApiItemKind.ConstructSignature:
|
||||
case ApiItemKind.Constructor:
|
||||
case ApiItemKind.IndexSignature:
|
||||
// These functional forms don't have a proper name, so we don't append the "()" suffix
|
||||
break;
|
||||
default:
|
||||
if (ApiParameterListMixin.isBaseClassOf(current)) {
|
||||
reversedParts.push('()');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reversedParts.push('.');
|
||||
}
|
||||
|
||||
reversedParts.push(current.displayName);
|
||||
}
|
||||
|
||||
return reversedParts.reverse().join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* If this item is an ApiPackage or has an ApiPackage as one of its parents, then that object is returned.
|
||||
* Otherwise undefined is returned.
|
||||
*/
|
||||
public getAssociatedPackage(): ApiPackage | undefined {
|
||||
for (let current: ApiItem | undefined = this; current !== undefined; current = current.parent) {
|
||||
if (current.kind === ApiItemKind.Package) {
|
||||
return current as ApiPackage;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this item is an ApiModel or has an ApiModel as one of its parents, then that object is returned.
|
||||
* Otherwise undefined is returned.
|
||||
*/
|
||||
public getAssociatedModel(): ApiModel | undefined {
|
||||
for (let current: ApiItem | undefined = this; current !== undefined; current = current.parent) {
|
||||
if (current.kind === ApiItemKind.Model) {
|
||||
return current as ApiModel;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A text string whose value determines the sort order that is automatically applied by the
|
||||
* {@link (ApiItemContainerMixin:interface)} class.
|
||||
*
|
||||
* @remarks
|
||||
* The value of this string is undocumented and may change at any time.
|
||||
* If {@link (ApiItemContainerMixin:interface).preserveMemberOrder} is enabled for the `ApiItem`'s parent,
|
||||
* then no sorting is performed, and this key is not used.
|
||||
* @virtual
|
||||
*/
|
||||
public getSortKey(): string {
|
||||
return this.containerKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* PRIVATE
|
||||
*
|
||||
* @privateRemarks
|
||||
* Allows ApiItemContainerMixin to assign the parent when the item is added to a container.
|
||||
* @internal
|
||||
*/
|
||||
public [apiItem_onParentChanged](parent: ApiItem | undefined): void {
|
||||
this._parent = parent;
|
||||
this._canonicalReference = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the cached object used by the `canonicalReference` property.
|
||||
*
|
||||
* @virtual
|
||||
*/
|
||||
protected buildCanonicalReference(): DeclarationReference {
|
||||
throw new InternalError('ApiItem.canonicalReference was not implemented by the child class');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This abstraction is used by the mixin pattern.
|
||||
* It describes a class type that inherits from {@link ApiItem}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiItemConstructor extends Constructor<ApiItem>, PropertiesOf<typeof ApiItem> {}
|
||||
88
packages/api-extractor-model/src/items/ApiPropertyItem.ts
Normal file
88
packages/api-extractor-model/src/items/ApiPropertyItem.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiOptionalMixin, type IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin.js';
|
||||
import { ApiReadonlyMixin, type IApiReadonlyMixinOptions } from '../mixins/ApiReadonlyMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import type { Excerpt, IExcerptTokenRange } from '../mixins/Excerpt.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem, type IApiDeclaredItemJson } from './ApiDeclaredItem.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiPropertyItem}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiPropertyItemOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiOptionalMixinOptions,
|
||||
IApiReadonlyMixinOptions,
|
||||
IApiDeclaredItemOptions {
|
||||
propertyTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
export interface IApiPropertyItemJson extends IApiDeclaredItemJson {
|
||||
propertyTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* The abstract base class for {@link ApiProperty} and {@link ApiPropertySignature}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class ApiPropertyItem extends ApiNameMixin(
|
||||
ApiReleaseTagMixin(ApiOptionalMixin(ApiReadonlyMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the type of the property.
|
||||
*/
|
||||
public readonly propertyTypeExcerpt: Excerpt;
|
||||
|
||||
public constructor(options: IApiPropertyItemOptions) {
|
||||
super(options);
|
||||
|
||||
this.propertyTypeExcerpt = this.buildExcerpt(options.propertyTypeTokenRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiPropertyItemOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiPropertyItemJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.propertyTypeTokenRange = jsonObject.propertyTypeTokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this property should be documented as an event.
|
||||
*
|
||||
* @remarks
|
||||
* The `@eventProperty` TSDoc modifier can be added to readonly properties to indicate that they return an
|
||||
* event object that event handlers can be attached to. The event-handling API is implementation-defined, but
|
||||
* typically the return type would be a class with members such as `addHandler()` and `removeHandler()`.
|
||||
* The documentation should display such properties under an "Events" heading instead of the
|
||||
* usual "Properties" heading.
|
||||
*/
|
||||
public get isEventProperty(): boolean {
|
||||
if (this.tsdocComment) {
|
||||
return this.tsdocComment.modifierTagSet.isEventProperty();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiPropertyItemJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.propertyTypeTokenRange = this.propertyTypeExcerpt.tokenRange;
|
||||
}
|
||||
}
|
||||
115
packages/api-extractor-model/src/mixins/ApiAbstractMixin.ts
Normal file
115
packages/api-extractor-model/src/mixins/ApiAbstractMixin.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiAbstractMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiAbstractMixinOptions extends IApiItemOptions {
|
||||
isAbstract: boolean;
|
||||
}
|
||||
|
||||
export interface IApiAbstractMixinJson extends IApiItemJson {
|
||||
isAbstract: boolean;
|
||||
}
|
||||
|
||||
const _isAbstract: unique symbol = Symbol('ApiAbstractMixin._isAbstract');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that have an abstract modifier.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiAbstractMixin extends ApiItem {
|
||||
/**
|
||||
* Indicates that the API item's value has an 'abstract' modifier.
|
||||
*/
|
||||
readonly isAbstract: boolean;
|
||||
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiAbstractMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiAbstractMixin:interface)}
|
||||
* functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiAbstractMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiAbstractMixin) {
|
||||
class MixedClass extends baseClass implements ApiAbstractMixin {
|
||||
public [_isAbstract]: boolean;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiAbstractMixinOptions = args[0];
|
||||
this[_isAbstract] = options.isAbstract;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiAbstractMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiAbstractMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.isAbstract = jsonObject.isAbstract || false;
|
||||
}
|
||||
|
||||
public get isAbstract(): boolean {
|
||||
return this[_isAbstract];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiAbstractMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.isAbstract = this.isAbstract;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiAbstractMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiAbstractMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiAbstractMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiAbstractMixin {
|
||||
return apiItem.hasOwnProperty(_isAbstract);
|
||||
}
|
||||
}
|
||||
143
packages/api-extractor-model/src/mixins/ApiExportedMixin.ts
Normal file
143
packages/api-extractor-model/src/mixins/ApiExportedMixin.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import { Navigation } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (IApiExportedMixinOptions:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiExportedMixinOptions extends IApiItemOptions {
|
||||
isExported: boolean;
|
||||
}
|
||||
|
||||
export interface IApiExportedMixinJson extends IApiItemJson {
|
||||
isExported: boolean;
|
||||
}
|
||||
|
||||
const _isExported: unique symbol = Symbol('ApiExportedMixin._isExported');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can be exported.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiExportedMixin extends ApiItem {
|
||||
/**
|
||||
* Whether the declaration is exported from its parent item container (i.e. either an `ApiEntryPoint` or an
|
||||
* `ApiNamespace`).
|
||||
*
|
||||
* @remarks
|
||||
* Suppose `index.ts` is your entry point:
|
||||
*
|
||||
* ```ts
|
||||
* // index.ts
|
||||
*
|
||||
* export class A {}
|
||||
* class B {}
|
||||
*
|
||||
* namespace n {
|
||||
* export class C {}
|
||||
* class D {}
|
||||
* }
|
||||
*
|
||||
* // file.ts
|
||||
* export class E {}
|
||||
* ```
|
||||
*
|
||||
* Classes `A` and `C` are both exported, while classes `B`, `D`, and `E` are not. `E` is exported from its
|
||||
* local file, but not from its parent item container (i.e. the entry point).
|
||||
*/
|
||||
readonly isExported: boolean;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiExportedMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiExportedMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiExportedMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiExportedMixin) {
|
||||
class MixedClass extends baseClass implements ApiExportedMixin {
|
||||
public [_isExported]: boolean;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiExportedMixinOptions = args[0];
|
||||
this[_isExported] = options.isExported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiExportedMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiExportedMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
const declarationReference: DeclarationReference = DeclarationReference.parse(jsonObject.canonicalReference);
|
||||
options.isExported = declarationReference.navigation === (Navigation.Exports as any); // ambient const enums suck...
|
||||
}
|
||||
|
||||
public get isExported(): boolean {
|
||||
return this[_isExported];
|
||||
}
|
||||
|
||||
/**
|
||||
* The `isExported` property is intentionally not serialized because the information is already present
|
||||
* in the item's `canonicalReference`.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiExportedMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiExportedMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiExportedMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiExportedMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiExportedMixin {
|
||||
return apiItem.hasOwnProperty(_isExported);
|
||||
}
|
||||
}
|
||||
130
packages/api-extractor-model/src/mixins/ApiInitializerMixin.ts
Normal file
130
packages/api-extractor-model/src/mixins/ApiInitializerMixin.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { InternalError } from '@rushstack/node-core-library';
|
||||
import { ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import type { IExcerptTokenRange, Excerpt } from './Excerpt.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (IApiInitializerMixinOptions:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiInitializerMixinOptions extends IApiItemOptions {
|
||||
initializerTokenRange?: IExcerptTokenRange | undefined;
|
||||
}
|
||||
|
||||
export interface IApiInitializerMixinJson extends IApiItemJson {
|
||||
initializerTokenRange?: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
const _initializerExcerpt: unique symbol = Symbol('ApiInitializerMixin._initializerExcerpt');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can have an initializer.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiInitializerMixin extends ApiItem {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the item's initializer.
|
||||
*/
|
||||
readonly initializerExcerpt?: Excerpt | undefined;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiInitializerMixinJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiInitializerMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiInitializerMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiInitializerMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiInitializerMixin) {
|
||||
class MixedClass extends baseClass implements ApiInitializerMixin {
|
||||
public [_initializerExcerpt]?: Excerpt;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiInitializerMixinOptions = args[0];
|
||||
|
||||
if (this instanceof ApiDeclaredItem) {
|
||||
if (options.initializerTokenRange) {
|
||||
this[_initializerExcerpt] = this.buildExcerpt(options.initializerTokenRange);
|
||||
}
|
||||
} else {
|
||||
throw new InternalError('ApiInitializerMixin expects a base class that inherits from ApiDeclaredItem');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiInitializerMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiInitializerMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.initializerTokenRange = jsonObject.initializerTokenRange;
|
||||
}
|
||||
|
||||
public get initializerExcerpt(): Excerpt | undefined {
|
||||
return this[_initializerExcerpt];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiInitializerMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
// Note that JSON does not support the "undefined" value, so we simply omit the field entirely if it is undefined
|
||||
if (this.initializerExcerpt) {
|
||||
jsonObject.initializerTokenRange = this.initializerExcerpt.tokenRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiInitializerMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiInitializerMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiInitializerMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiInitializerMixin {
|
||||
return apiItem.hasOwnProperty(_initializerExcerpt);
|
||||
}
|
||||
}
|
||||
562
packages/api-extractor-model/src/mixins/ApiItemContainerMixin.ts
Normal file
562
packages/api-extractor-model/src/mixins/ApiItemContainerMixin.ts
Normal file
@@ -0,0 +1,562 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { InternalError } from '@rushstack/node-core-library';
|
||||
import {
|
||||
ApiItem,
|
||||
apiItem_onParentChanged,
|
||||
type IApiItemJson,
|
||||
type IApiItemOptions,
|
||||
type IApiItemConstructor,
|
||||
ApiItemKind,
|
||||
} from '../items/ApiItem.js';
|
||||
import type { ApiClass } from '../model/ApiClass.js';
|
||||
import type { ApiInterface } from '../model/ApiInterface.js';
|
||||
import type { ApiModel } from '../model/ApiModel.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import type { HeritageType } from '../model/HeritageType.js';
|
||||
import type { IResolveDeclarationReferenceResult } from '../model/ModelReferenceResolver.js';
|
||||
import { ApiNameMixin } from './ApiNameMixin.js';
|
||||
import { type ExcerptToken, ExcerptTokenKind } from './Excerpt.js';
|
||||
import { type IFindApiItemsResult, type IFindApiItemsMessage, FindApiItemsMessageId } from './IFindApiItemsResult.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiItemContainerMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiItemContainerMixinOptions extends IApiItemOptions {
|
||||
members?: ApiItem[] | undefined;
|
||||
preserveMemberOrder?: boolean | undefined;
|
||||
}
|
||||
|
||||
export interface IApiItemContainerJson extends IApiItemJson {
|
||||
members: IApiItemJson[];
|
||||
preserveMemberOrder?: boolean;
|
||||
}
|
||||
|
||||
const _members: unique symbol = Symbol('ApiItemContainerMixin._members');
|
||||
const _membersSorted: unique symbol = Symbol('ApiItemContainerMixin._membersSorted');
|
||||
const _membersByContainerKey: unique symbol = Symbol('ApiItemContainerMixin._membersByContainerKey');
|
||||
const _membersByName: unique symbol = Symbol('ApiItemContainerMixin._membersByName');
|
||||
const _membersByKind: unique symbol = Symbol('ApiItemContainerMixin._membersByKind');
|
||||
const _preserveMemberOrder: unique symbol = Symbol('ApiItemContainerMixin._preserveMemberOrder');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that act as containers for other child items.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
*
|
||||
* Examples of `ApiItemContainerMixin` child classes include `ApiModel`, `ApiPackage`, `ApiEntryPoint`,
|
||||
* and `ApiEnum`. But note that `Parameter` is not considered a "member" of an `ApiMethod`; this relationship
|
||||
* is modeled using {@link (ApiParameterListMixin:interface).parameters} instead
|
||||
* of {@link ApiItem.members}.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiItemContainerMixin extends ApiItem {
|
||||
/**
|
||||
* For a given member of this container, return its `ApiItem.getMergedSiblings()` list.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
_getMergedSiblingsForMember(memberApiItem: ApiItem): readonly ApiItem[];
|
||||
|
||||
/**
|
||||
* Adds a new member to the container.
|
||||
*
|
||||
* @remarks
|
||||
* An ApiItem cannot be added to more than one container.
|
||||
*/
|
||||
addMember(member: ApiItem): void;
|
||||
|
||||
/**
|
||||
* Returns a list of members with the specified name.
|
||||
*/
|
||||
findMembersByName(name: string): readonly ApiItem[];
|
||||
|
||||
/**
|
||||
* Finds all of the ApiItem's immediate and inherited members by walking up the inheritance tree.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Given the following class heritage:
|
||||
*
|
||||
* ```
|
||||
* export class A {
|
||||
* public a: number|boolean;
|
||||
* }
|
||||
*
|
||||
* export class B extends A {
|
||||
* public a: number;
|
||||
* public b: string;
|
||||
* }
|
||||
*
|
||||
* export class C extends B {
|
||||
* public c: boolean;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Calling `findMembersWithInheritance` on `C` will return `B.a`, `B.b`, and `C.c`. Calling the
|
||||
* method on `B` will return `B.a` and `B.b`. And calling the method on `A` will return just
|
||||
* `A.a`.
|
||||
*
|
||||
* The inherited members returned by this method may be incomplete. If so, there will be a flag
|
||||
* on the result object indicating this as well as messages explaining the errors in more detail.
|
||||
* Some scenarios include:
|
||||
*
|
||||
* - Interface extending from a type alias.
|
||||
*
|
||||
* - Class extending from a variable.
|
||||
*
|
||||
* - Extending from a declaration not present in the model (e.g. external package).
|
||||
*
|
||||
* - Extending from an unexported declaration (e.g. ae-forgotten-export). Common in mixin
|
||||
* patterns.
|
||||
*
|
||||
* - Unexpected runtime errors...
|
||||
*
|
||||
* Lastly, be aware that the types of inherited members are returned with respect to their
|
||||
* defining class as opposed to with respect to the inheriting class. For example, consider
|
||||
* the following:
|
||||
*
|
||||
* ```
|
||||
* export class A<T> {
|
||||
* public a: T;
|
||||
* }
|
||||
*
|
||||
* export class B extends A<number> {}
|
||||
* ```
|
||||
*
|
||||
* When called on `B`, this method will return `B.a` with type `T` as opposed to type
|
||||
* `number`, although the latter is more accurate.
|
||||
*/
|
||||
findMembersWithInheritance(): IFindApiItemsResult;
|
||||
|
||||
/**
|
||||
* Disables automatic sorting of {@link ApiItem.members}.
|
||||
*
|
||||
* @remarks
|
||||
* By default `ApiItemContainerMixin` will automatically sort its members according to their
|
||||
* {@link ApiItem.getSortKey} string, which provides a standardized mostly alphabetical ordering
|
||||
* that is appropriate for most API items. When loading older .api.json files the automatic sorting
|
||||
* is reapplied and may update the ordering.
|
||||
*
|
||||
* Set `preserveMemberOrder` to true to disable automatic sorting for this container; instead, the
|
||||
* members will retain whatever ordering appeared in the {@link IApiItemContainerMixinOptions.members} array.
|
||||
* The `preserveMemberOrder` option is saved in the .api.json file.
|
||||
*/
|
||||
readonly preserveMemberOrder: boolean;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
|
||||
/**
|
||||
* Attempts to retrieve a member using its containerKey, or returns `undefined` if no matching member was found.
|
||||
*
|
||||
* @remarks
|
||||
* Use the `getContainerKey()` static member to construct the key. Each subclass has a different implementation
|
||||
* of this function, according to the aspects that are important for identifying it.
|
||||
*
|
||||
* See {@link ApiItem.containerKey} for more information.
|
||||
*/
|
||||
tryGetMemberByKey(containerKey: string): ApiItem | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link ApiDeclaredItem}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiItemContainerMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiItemContainerMixin) {
|
||||
class MixedClass extends baseClass implements ApiItemContainerMixin {
|
||||
public readonly [_members]: ApiItem[];
|
||||
|
||||
public [_membersSorted]: boolean;
|
||||
|
||||
public [_membersByContainerKey]: Map<string, ApiItem>;
|
||||
|
||||
public [_preserveMemberOrder]: boolean;
|
||||
|
||||
// For members of this container that extend ApiNameMixin, this stores the list of members with a given name.
|
||||
// Examples include merged declarations, overloaded functions, etc.
|
||||
public [_membersByName]: Map<string, ApiItem[]> | undefined;
|
||||
|
||||
// For members of this container that do NOT extend ApiNameMixin, this stores the list of members
|
||||
// that share a common ApiItemKind. Examples include overloaded constructors or index signatures.
|
||||
public [_membersByKind]: Map<string, ApiItem[]> | undefined; // key is ApiItemKind
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
const options: IApiItemContainerMixinOptions = args[0] as IApiItemContainerMixinOptions;
|
||||
|
||||
this[_members] = [];
|
||||
this[_membersSorted] = false;
|
||||
this[_membersByContainerKey] = new Map<string, ApiItem>();
|
||||
this[_preserveMemberOrder] = options.preserveMemberOrder ?? false;
|
||||
|
||||
if (options.members) {
|
||||
for (const member of options.members) {
|
||||
this.addMember(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiItemContainerMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiItemContainerJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
options.preserveMemberOrder = jsonObject.preserveMemberOrder;
|
||||
options.members = [];
|
||||
for (const memberObject of jsonObject.members) {
|
||||
options.members.push(ApiItem.deserialize(memberObject, context));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get members(): readonly ApiItem[] {
|
||||
if (!this[_membersSorted] && !this[_preserveMemberOrder]) {
|
||||
this[_members].sort((x, y) => x.getSortKey().localeCompare(y.getSortKey()));
|
||||
this[_membersSorted] = true;
|
||||
}
|
||||
|
||||
return this[_members];
|
||||
}
|
||||
|
||||
public get preserveMemberOrder(): boolean {
|
||||
return this[_preserveMemberOrder];
|
||||
}
|
||||
|
||||
public addMember(member: ApiItem): void {
|
||||
if (this[_membersByContainerKey].has(member.containerKey)) {
|
||||
throw new Error(
|
||||
`Another member has already been added with the same name (${member.displayName})` +
|
||||
` and containerKey (${member.containerKey})`,
|
||||
);
|
||||
}
|
||||
|
||||
const existingParent: ApiItem | undefined = member.parent;
|
||||
if (existingParent !== undefined) {
|
||||
throw new Error(`This item has already been added to another container: "${existingParent.displayName}"`);
|
||||
}
|
||||
|
||||
this[_members].push(member);
|
||||
this[_membersByName] = undefined; // invalidate the lookup
|
||||
this[_membersByKind] = undefined; // invalidate the lookup
|
||||
this[_membersSorted] = false;
|
||||
this[_membersByContainerKey].set(member.containerKey, member);
|
||||
|
||||
member[apiItem_onParentChanged](this);
|
||||
}
|
||||
|
||||
public tryGetMemberByKey(containerKey: string): ApiItem | undefined {
|
||||
return this[_membersByContainerKey].get(containerKey);
|
||||
}
|
||||
|
||||
public findMembersByName(name: string): readonly ApiItem[] {
|
||||
this._ensureMemberMaps();
|
||||
return this[_membersByName]!.get(name) ?? [];
|
||||
}
|
||||
|
||||
public findMembersWithInheritance(): IFindApiItemsResult {
|
||||
const messages: IFindApiItemsMessage[] = [];
|
||||
let maybeIncompleteResult = false;
|
||||
|
||||
// For API items that don't support inheritance, this method just returns the item's
|
||||
// immediate members.
|
||||
switch (this.kind) {
|
||||
case ApiItemKind.Class:
|
||||
case ApiItemKind.Interface:
|
||||
break;
|
||||
default: {
|
||||
return {
|
||||
items: this.members.concat(),
|
||||
messages,
|
||||
maybeIncompleteResult,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const membersByName: Map<string, ApiItem[]> = new Map();
|
||||
const membersByKind: Map<ApiItemKind, ApiItem[]> = new Map();
|
||||
|
||||
const toVisit: ApiItem[] = [];
|
||||
let next: ApiItem | undefined = this;
|
||||
|
||||
while (next) {
|
||||
const membersToAdd: ApiItem[] = [];
|
||||
|
||||
// For each member, check to see if we've already seen a member with the same name
|
||||
// previously in the inheritance tree. If so, we know we won't inherit it, and thus
|
||||
// do not add it to our `membersToAdd` array.
|
||||
for (const member of next.members) {
|
||||
// We add the to-be-added members to an intermediate array instead of immediately
|
||||
// to the maps themselves to support method overloads with the same name.
|
||||
if (ApiNameMixin.isBaseClassOf(member)) {
|
||||
if (!membersByName.has(member.name)) {
|
||||
membersToAdd.push(member);
|
||||
}
|
||||
} else if (!membersByKind.has(member.kind)) {
|
||||
membersToAdd.push(member);
|
||||
}
|
||||
}
|
||||
|
||||
for (const member of membersToAdd) {
|
||||
if (ApiNameMixin.isBaseClassOf(member)) {
|
||||
const members: ApiItem[] = membersByName.get(member.name) ?? [];
|
||||
members.push(member);
|
||||
membersByName.set(member.name, members);
|
||||
} else {
|
||||
const members: ApiItem[] = membersByKind.get(member.kind) ?? [];
|
||||
members.push(member);
|
||||
membersByKind.set(member.kind, members);
|
||||
}
|
||||
}
|
||||
|
||||
// Interfaces can extend multiple interfaces, so iterate through all of them.
|
||||
const extendedItems: ApiItem[] = [];
|
||||
let extendsTypes: readonly HeritageType[] | undefined;
|
||||
|
||||
switch (next.kind) {
|
||||
case ApiItemKind.Class: {
|
||||
const apiClass: ApiClass = next as ApiClass;
|
||||
extendsTypes = apiClass.extendsType ? [apiClass.extendsType] : [];
|
||||
break;
|
||||
}
|
||||
|
||||
case ApiItemKind.Interface: {
|
||||
const apiInterface: ApiInterface = next as ApiInterface;
|
||||
extendsTypes = apiInterface.extendsTypes;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (extendsTypes === undefined) {
|
||||
messages.push({
|
||||
messageId: FindApiItemsMessageId.UnsupportedKind,
|
||||
text: `Unable to analyze references of API item ${next.displayName} because it is of unsupported kind ${next.kind}`,
|
||||
});
|
||||
maybeIncompleteResult = true;
|
||||
next = toVisit.shift();
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const extendsType of extendsTypes) {
|
||||
// We want to find the reference token associated with the actual inherited declaration.
|
||||
// In every case we support, this is the first reference token. For example:
|
||||
//
|
||||
// ```
|
||||
// export class A extends B {}
|
||||
// ^
|
||||
// export class A extends B<C> {}
|
||||
// ^
|
||||
// export class A extends B.C {}
|
||||
// ^^^
|
||||
// ```
|
||||
const firstReferenceToken: ExcerptToken | undefined = extendsType.excerpt.spannedTokens.find(
|
||||
(token: ExcerptToken) => {
|
||||
return token.kind === ExcerptTokenKind.Reference && token.canonicalReference;
|
||||
},
|
||||
);
|
||||
|
||||
if (!firstReferenceToken) {
|
||||
messages.push({
|
||||
messageId: FindApiItemsMessageId.ExtendsClauseMissingReference,
|
||||
text: `Unable to analyze extends clause ${extendsType.excerpt.text} of API item ${next.displayName} because no canonical reference was found`,
|
||||
});
|
||||
maybeIncompleteResult = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const apiModel: ApiModel | undefined = this.getAssociatedModel();
|
||||
if (!apiModel) {
|
||||
messages.push({
|
||||
messageId: FindApiItemsMessageId.NoAssociatedApiModel,
|
||||
text: `Unable to analyze references of API item ${next.displayName} because it is not associated with an ApiModel`,
|
||||
});
|
||||
maybeIncompleteResult = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
const canonicalReference: DeclarationReference = firstReferenceToken.canonicalReference!;
|
||||
const apiItemResult: IResolveDeclarationReferenceResult = apiModel.resolveDeclarationReference(
|
||||
canonicalReference,
|
||||
undefined,
|
||||
);
|
||||
|
||||
const apiItem: ApiItem | undefined = apiItemResult.resolvedApiItem;
|
||||
if (!apiItem) {
|
||||
messages.push({
|
||||
messageId: FindApiItemsMessageId.DeclarationResolutionFailed,
|
||||
text: `Unable to resolve declaration reference within API item ${next.displayName}: ${apiItemResult.errorMessage}`,
|
||||
});
|
||||
maybeIncompleteResult = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
extendedItems.push(apiItem);
|
||||
}
|
||||
|
||||
// For classes, this array will only have one item. For interfaces, there may be multiple items. Sort the array
|
||||
// into alphabetical order before adding to our list of API items to visit. This ensures that in the case
|
||||
// of multiple interface inheritance, a member inherited from multiple interfaces is attributed to the interface
|
||||
// earlier in alphabetical order (as opposed to source order).
|
||||
//
|
||||
// For example, in the code block below, `Bar.x` is reported as the inherited item, not `Foo.x`.
|
||||
//
|
||||
// ```
|
||||
// interface Foo {
|
||||
// public x: string;
|
||||
// }
|
||||
//
|
||||
// interface Bar {
|
||||
// public x: string;
|
||||
// }
|
||||
//
|
||||
// interface FooBar extends Foo, Bar {}
|
||||
// ```
|
||||
extendedItems.sort((x: ApiItem, y: ApiItem) => x.getSortKey().localeCompare(y.getSortKey()));
|
||||
|
||||
toVisit.push(...extendedItems);
|
||||
next = toVisit.shift();
|
||||
}
|
||||
|
||||
const items: ApiItem[] = [];
|
||||
for (const members of membersByName.values()) {
|
||||
items.push(...members);
|
||||
}
|
||||
|
||||
for (const members of membersByKind.values()) {
|
||||
items.push(...members);
|
||||
}
|
||||
|
||||
items.sort((x: ApiItem, y: ApiItem) => x.getSortKey().localeCompare(y.getSortKey()));
|
||||
|
||||
return {
|
||||
items,
|
||||
messages,
|
||||
maybeIncompleteResult,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public _getMergedSiblingsForMember(memberApiItem: ApiItem): readonly ApiItem[] {
|
||||
this._ensureMemberMaps();
|
||||
let result: ApiItem[] | undefined;
|
||||
if (ApiNameMixin.isBaseClassOf(memberApiItem)) {
|
||||
result = this[_membersByName]!.get(memberApiItem.name);
|
||||
} else {
|
||||
result = this[_membersByKind]!.get(memberApiItem.kind);
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
throw new InternalError('Item was not found in the _membersByName/_membersByKind lookup');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public _ensureMemberMaps(): void {
|
||||
// Build the _membersByName and _membersByKind tables if they don't already exist
|
||||
if (this[_membersByName] === undefined) {
|
||||
const membersByName: Map<string, ApiItem[]> = new Map<string, ApiItem[]>();
|
||||
const membersByKind: Map<string, ApiItem[]> = new Map<string, ApiItem[]>();
|
||||
|
||||
for (const member of this[_members]) {
|
||||
let map: Map<ApiItemKind, ApiItem[]> | Map<string, ApiItem[]>;
|
||||
let key: ApiItemKind | string;
|
||||
|
||||
if (ApiNameMixin.isBaseClassOf(member)) {
|
||||
map = membersByName;
|
||||
key = member.name;
|
||||
} else {
|
||||
map = membersByKind;
|
||||
key = member.kind;
|
||||
}
|
||||
|
||||
let list: ApiItem[] | undefined = map.get(key);
|
||||
if (list === undefined) {
|
||||
list = [];
|
||||
map.set(key, list);
|
||||
}
|
||||
|
||||
list.push(member);
|
||||
}
|
||||
|
||||
this[_membersByName] = membersByName;
|
||||
this[_membersByKind] = membersByKind;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiItemContainerJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
const memberObjects: IApiItemJson[] = [];
|
||||
|
||||
for (const member of this.members) {
|
||||
const memberJsonObject: Partial<IApiItemJson> = {};
|
||||
member.serializeInto(memberJsonObject);
|
||||
memberObjects.push(memberJsonObject as IApiItemJson);
|
||||
}
|
||||
|
||||
jsonObject.preserveMemberOrder = this.preserveMemberOrder;
|
||||
jsonObject.members = memberObjects;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiItemContainerMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
export namespace ApiItemContainerMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiItemContainerMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiItemContainerMixin {
|
||||
return apiItem.hasOwnProperty(_members);
|
||||
}
|
||||
}
|
||||
128
packages/api-extractor-model/src/mixins/ApiNameMixin.ts
Normal file
128
packages/api-extractor-model/src/mixins/ApiNameMixin.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (IApiNameMixinOptions:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiNameMixinOptions extends IApiItemOptions {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface IApiNameMixinJson extends IApiItemJson {
|
||||
name: string;
|
||||
}
|
||||
|
||||
const _name: unique symbol = Symbol('ApiNameMixin._name');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that have a name. For example, a class has a name, but a class constructor
|
||||
* does not.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiNameMixin extends ApiItem {
|
||||
/**
|
||||
* The exported name of this API item.
|
||||
*
|
||||
* @remarks
|
||||
* Note that due tue type aliasing, the exported name may be different from the locally declared name.
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiNameMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiNameMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiNameMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiNameMixin) {
|
||||
class MixedClass extends baseClass implements ApiNameMixin {
|
||||
public readonly [_name]: string;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiNameMixinOptions = args[0];
|
||||
this[_name] = options.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiNameMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiNameMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.name = jsonObject.name;
|
||||
}
|
||||
|
||||
public get name(): string {
|
||||
return this[_name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get displayName(): string {
|
||||
return this[_name];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiNameMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.name = this.name;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiNameMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiNameMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiNameMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiNameMixin {
|
||||
return apiItem.hasOwnProperty(_name);
|
||||
}
|
||||
}
|
||||
127
packages/api-extractor-model/src/mixins/ApiOptionalMixin.ts
Normal file
127
packages/api-extractor-model/src/mixins/ApiOptionalMixin.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (IApiOptionalMixinOptions:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiOptionalMixinOptions extends IApiItemOptions {
|
||||
isOptional: boolean;
|
||||
}
|
||||
|
||||
export interface IApiOptionalMixinJson extends IApiItemJson {
|
||||
isOptional: boolean;
|
||||
}
|
||||
|
||||
const _isOptional: unique symbol = Symbol('ApiOptionalMixin._isOptional');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can be marked as optional by appending a `?` to them.
|
||||
* For example, a property of an interface can be optional.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiOptionalMixin extends ApiItem {
|
||||
/**
|
||||
* True if this is an optional property.
|
||||
*
|
||||
* @remarks
|
||||
* For example:
|
||||
* ```ts
|
||||
* interface X {
|
||||
* y: string; // not optional
|
||||
* z?: string; // optional
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
readonly isOptional: boolean;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiOptionalMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiOptionalMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiOptionalMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiOptionalMixin) {
|
||||
class MixedClass extends baseClass implements ApiOptionalMixin {
|
||||
public [_isOptional]: boolean;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiOptionalMixinOptions = args[0];
|
||||
this[_isOptional] = Boolean(options.isOptional);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiOptionalMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiOptionalMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.isOptional = Boolean(jsonObject.isOptional);
|
||||
}
|
||||
|
||||
public get isOptional(): boolean {
|
||||
return this[_isOptional];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiOptionalMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.isOptional = this.isOptional;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optional members for {@link (ApiOptionalMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiOptionalMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiOptionalMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiOptionalMixin {
|
||||
return apiItem.hasOwnProperty(_isOptional);
|
||||
}
|
||||
}
|
||||
202
packages/api-extractor-model/src/mixins/ApiParameterListMixin.ts
Normal file
202
packages/api-extractor-model/src/mixins/ApiParameterListMixin.ts
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { InternalError } from '@rushstack/node-core-library';
|
||||
import { ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import { Parameter } from '../model/Parameter.js';
|
||||
import type { IExcerptTokenRange } from './Excerpt.js';
|
||||
|
||||
/**
|
||||
* Represents parameter information that is part of {@link IApiParameterListMixinOptions}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiParameterOptions {
|
||||
isOptional: boolean;
|
||||
isRest: boolean;
|
||||
parameterName: string;
|
||||
parameterTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiParameterListMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiParameterListMixinOptions extends IApiItemOptions {
|
||||
overloadIndex: number;
|
||||
parameters: IApiParameterOptions[];
|
||||
}
|
||||
|
||||
export interface IApiParameterListJson extends IApiItemJson {
|
||||
overloadIndex: number;
|
||||
parameters: IApiParameterOptions[];
|
||||
}
|
||||
|
||||
const _overloadIndex: unique symbol = Symbol('ApiParameterListMixin._overloadIndex');
|
||||
const _parameters: unique symbol = Symbol('ApiParameterListMixin._parameters');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can have function parameters (but not necessarily a return value).
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiParameterListMixin extends ApiItem {
|
||||
/**
|
||||
* When a function has multiple overloaded declarations, this one-based integer index can be used to uniquely
|
||||
* identify them.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Consider this overloaded declaration:
|
||||
*
|
||||
* ```ts
|
||||
* export namespace Versioning {
|
||||
* // TSDoc: Versioning.(addVersions:1)
|
||||
* export function addVersions(x: number, y: number): number;
|
||||
*
|
||||
* // TSDoc: Versioning.(addVersions:2)
|
||||
* export function addVersions(x: string, y: string): string;
|
||||
*
|
||||
* // (implementation)
|
||||
* export function addVersions(x: number|string, y: number|string): number|string {
|
||||
* // . . .
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In the above example, there are two overloaded declarations. The overload using numbers will have
|
||||
* `overloadIndex = 1`. The overload using strings will have `overloadIndex = 2`. The third declaration that
|
||||
* accepts all possible inputs is considered part of the implementation, and is not processed by API Extractor.
|
||||
*/
|
||||
readonly overloadIndex: number;
|
||||
|
||||
/**
|
||||
* The function parameters.
|
||||
*/
|
||||
readonly parameters: readonly Parameter[];
|
||||
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiParameterListMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiParameterListMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiParameterListMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiParameterListMixin) {
|
||||
class MixedClass extends baseClass implements ApiParameterListMixin {
|
||||
public readonly [_overloadIndex]: number;
|
||||
|
||||
public readonly [_parameters]: Parameter[];
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiParameterListMixinOptions = args[0];
|
||||
this[_overloadIndex] = options.overloadIndex;
|
||||
|
||||
this[_parameters] = [];
|
||||
|
||||
if (this instanceof ApiDeclaredItem) {
|
||||
if (options.parameters) {
|
||||
for (const parameterOptions of options.parameters) {
|
||||
const parameter: Parameter = new Parameter({
|
||||
name: parameterOptions.parameterName,
|
||||
parameterTypeExcerpt: this.buildExcerpt(parameterOptions.parameterTypeTokenRange),
|
||||
// Prior to ApiJsonSchemaVersion.V_1005 this input will be undefined
|
||||
isOptional: Boolean(parameterOptions.isOptional),
|
||||
isRest: Boolean(parameterOptions.isRest),
|
||||
parent: this,
|
||||
});
|
||||
|
||||
this[_parameters].push(parameter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new InternalError('ApiReturnTypeMixin expects a base class that inherits from ApiDeclaredItem');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiParameterListMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiParameterListJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.overloadIndex = jsonObject.overloadIndex;
|
||||
options.parameters = jsonObject.parameters || [];
|
||||
}
|
||||
|
||||
public get overloadIndex(): number {
|
||||
return this[_overloadIndex];
|
||||
}
|
||||
|
||||
public get parameters(): readonly Parameter[] {
|
||||
return this[_parameters];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiParameterListJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.overloadIndex = this.overloadIndex;
|
||||
|
||||
const parameterObjects: IApiParameterOptions[] = [];
|
||||
for (const parameter of this.parameters) {
|
||||
parameterObjects.push({
|
||||
parameterName: parameter.name,
|
||||
parameterTypeTokenRange: parameter.parameterTypeExcerpt.tokenRange,
|
||||
isOptional: parameter.isOptional,
|
||||
isRest: parameter.isRest,
|
||||
});
|
||||
}
|
||||
|
||||
jsonObject.parameters = parameterObjects;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiParameterListMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiParameterListMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiParameterListMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiParameterListMixin {
|
||||
return apiItem.hasOwnProperty(_parameters);
|
||||
}
|
||||
}
|
||||
117
packages/api-extractor-model/src/mixins/ApiProtectedMixin.ts
Normal file
117
packages/api-extractor-model/src/mixins/ApiProtectedMixin.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (IApiProtectedMixinOptions:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiProtectedMixinOptions extends IApiItemOptions {
|
||||
isProtected: boolean;
|
||||
}
|
||||
|
||||
export interface IApiProtectedMixinJson extends IApiItemJson {
|
||||
isProtected: boolean;
|
||||
}
|
||||
|
||||
const _isProtected: unique symbol = Symbol('ApiProtectedMixin._isProtected');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can have the TypeScript `protected` keyword applied to them.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiProtectedMixin extends ApiItem {
|
||||
/**
|
||||
* Whether the declaration has the TypeScript `protected` keyword.
|
||||
*/
|
||||
readonly isProtected: boolean;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiProtectedMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiProtectedMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiProtectedMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiProtectedMixin) {
|
||||
class MixedClass extends baseClass implements ApiProtectedMixin {
|
||||
public [_isProtected]: boolean;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiProtectedMixinOptions = args[0];
|
||||
this[_isProtected] = options.isProtected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiProtectedMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiProtectedMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.isProtected = jsonObject.isProtected;
|
||||
}
|
||||
|
||||
public get isProtected(): boolean {
|
||||
return this[_isProtected];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiProtectedMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.isProtected = this.isProtected;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiProtectedMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiProtectedMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiProtectedMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiProtectedMixin {
|
||||
return apiItem.hasOwnProperty(_isProtected);
|
||||
}
|
||||
}
|
||||
137
packages/api-extractor-model/src/mixins/ApiReadonlyMixin.ts
Normal file
137
packages/api-extractor-model/src/mixins/ApiReadonlyMixin.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiReadonlyMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiReadonlyMixinOptions extends IApiItemOptions {
|
||||
isReadonly: boolean;
|
||||
}
|
||||
|
||||
export interface IApiReadonlyMixinJson extends IApiItemJson {
|
||||
isReadonly: boolean;
|
||||
}
|
||||
|
||||
const _isReadonly: unique symbol = Symbol('ApiReadonlyMixin._isReadonly');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that cannot be modified after instantiation.
|
||||
* Examples such as the readonly modifier and only having a getter but no setter.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiReadonlyMixin extends ApiItem {
|
||||
/**
|
||||
* Indicates that the API item's value cannot be assigned by an external consumer.
|
||||
*
|
||||
* @remarks
|
||||
* Examples of API items that would be considered "read only" by API Extractor:
|
||||
*
|
||||
* - A class or interface's property that has the `readonly` modifier.
|
||||
*
|
||||
* - A variable that has the `const` modifier.
|
||||
*
|
||||
* - A property or variable whose TSDoc comment includes the `@readonly` tag.
|
||||
*
|
||||
* - A property declaration with a getter but no setter.
|
||||
*
|
||||
* Note that if the `readonly` keyword appears in a type annotation, this does not
|
||||
* guarantee that that the API item will be considered readonly. For example:
|
||||
*
|
||||
* ```ts
|
||||
* declare class C {
|
||||
* // isReadonly=false in this case, because C.x is assignable
|
||||
* public x: readonly string[];
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
readonly isReadonly: boolean;
|
||||
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiReadonlyMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiReadonlyMixin:interface)}
|
||||
* functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiReadonlyMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiReadonlyMixin) {
|
||||
class MixedClass extends baseClass implements ApiReadonlyMixin {
|
||||
public [_isReadonly]: boolean;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiReadonlyMixinOptions = args[0];
|
||||
this[_isReadonly] = options.isReadonly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiReadonlyMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiReadonlyMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.isReadonly = jsonObject.isReadonly || false;
|
||||
}
|
||||
|
||||
public get isReadonly(): boolean {
|
||||
return this[_isReadonly];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiReadonlyMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.isReadonly = this.isReadonly;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiReadonlyMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiReadonlyMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiReadonlyMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiReadonlyMixin {
|
||||
return apiItem.hasOwnProperty(_isReadonly);
|
||||
}
|
||||
}
|
||||
132
packages/api-extractor-model/src/mixins/ApiReleaseTagMixin.ts
Normal file
132
packages/api-extractor-model/src/mixins/ApiReleaseTagMixin.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { Enum } from '@rushstack/node-core-library';
|
||||
import { ReleaseTag } from '../aedoc/ReleaseTag.js';
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiReleaseTagMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiReleaseTagMixinOptions extends IApiItemOptions {
|
||||
releaseTag: ReleaseTag;
|
||||
}
|
||||
|
||||
export interface IApiReleaseTagMixinJson extends IApiItemJson {
|
||||
releaseTag: string;
|
||||
}
|
||||
|
||||
const _releaseTag: unique symbol = Symbol('ApiReleaseTagMixin._releaseTag');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can be attributed with a TSDoc tag such as `@internal`,
|
||||
* `@alpha`, `@beta`, or `@public`. These "release tags" indicate the support level for an API.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiReleaseTagMixin extends ApiItem {
|
||||
/**
|
||||
* The effective release tag for this declaration. If it is not explicitly specified, the value may be
|
||||
* inherited from a containing declaration.
|
||||
*
|
||||
* @remarks
|
||||
* For example, an `ApiEnumMember` may inherit its release tag from the containing `ApiEnum`.
|
||||
*/
|
||||
readonly releaseTag: ReleaseTag;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiReleaseTagMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiReleaseTagMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiReleaseTagMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiReleaseTagMixin) {
|
||||
class MixedClass extends baseClass implements ApiReleaseTagMixin {
|
||||
public [_releaseTag]: ReleaseTag;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiReleaseTagMixinOptions = args[0];
|
||||
this[_releaseTag] = options.releaseTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiReleaseTagMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiReleaseTagMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
const deserializedReleaseTag: ReleaseTag | undefined = Enum.tryGetValueByKey<ReleaseTag>(
|
||||
ReleaseTag as any,
|
||||
jsonObject.releaseTag,
|
||||
);
|
||||
if (deserializedReleaseTag === undefined) {
|
||||
throw new Error(`Failed to deserialize release tag ${JSON.stringify(jsonObject.releaseTag)}`);
|
||||
}
|
||||
|
||||
options.releaseTag = deserializedReleaseTag;
|
||||
}
|
||||
|
||||
public get releaseTag(): ReleaseTag {
|
||||
return this[_releaseTag];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiReleaseTagMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.releaseTag = ReleaseTag[this.releaseTag];
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiReleaseTagMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiReleaseTagMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiReleaseTagMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiReleaseTagMixin {
|
||||
return apiItem.hasOwnProperty(_releaseTag);
|
||||
}
|
||||
}
|
||||
125
packages/api-extractor-model/src/mixins/ApiReturnTypeMixin.ts
Normal file
125
packages/api-extractor-model/src/mixins/ApiReturnTypeMixin.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { InternalError } from '@rushstack/node-core-library';
|
||||
import { ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import type { IExcerptTokenRange, Excerpt } from './Excerpt.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiReturnTypeMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiReturnTypeMixinOptions extends IApiItemOptions {
|
||||
returnTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
export interface IApiReturnTypeMixinJson extends IApiItemJson {
|
||||
returnTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
const _returnTypeExcerpt: unique symbol = Symbol('ApiReturnTypeMixin._returnTypeExcerpt');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that are functions that return a value.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiReturnTypeMixin extends ApiItem {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the type of the function's return value.
|
||||
*/
|
||||
readonly returnTypeExcerpt: Excerpt;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiReturnTypeMixinJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiReturnTypeMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiReturnTypeMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiReturnTypeMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiReturnTypeMixin) {
|
||||
class MixedClass extends baseClass implements ApiReturnTypeMixin {
|
||||
public [_returnTypeExcerpt]: Excerpt;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiReturnTypeMixinOptions = args[0];
|
||||
|
||||
if (this instanceof ApiDeclaredItem) {
|
||||
this[_returnTypeExcerpt] = this.buildExcerpt(options.returnTypeTokenRange);
|
||||
} else {
|
||||
throw new InternalError('ApiReturnTypeMixin expects a base class that inherits from ApiDeclaredItem');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiReturnTypeMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiReturnTypeMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.returnTypeTokenRange = jsonObject.returnTypeTokenRange;
|
||||
}
|
||||
|
||||
public get returnTypeExcerpt(): Excerpt {
|
||||
return this[_returnTypeExcerpt];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiReturnTypeMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.returnTypeTokenRange = this.returnTypeExcerpt.tokenRange;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiReturnTypeMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiReturnTypeMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiReturnTypeMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiReturnTypeMixin {
|
||||
return apiItem.hasOwnProperty(_returnTypeExcerpt);
|
||||
}
|
||||
}
|
||||
117
packages/api-extractor-model/src/mixins/ApiStaticMixin.ts
Normal file
117
packages/api-extractor-model/src/mixins/ApiStaticMixin.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (IApiStaticMixinOptions:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiStaticMixinOptions extends IApiItemOptions {
|
||||
isStatic: boolean;
|
||||
}
|
||||
|
||||
export interface IApiStaticMixinJson extends IApiItemJson {
|
||||
isStatic: boolean;
|
||||
}
|
||||
|
||||
const _isStatic: unique symbol = Symbol('ApiStaticMixin._isStatic');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can have the TypeScript `static` keyword applied to them.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiStaticMixin extends ApiItem {
|
||||
/**
|
||||
* Whether the declaration has the TypeScript `static` keyword.
|
||||
*/
|
||||
readonly isStatic: boolean;
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiStaticMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiStaticMixin:interface)} functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiStaticMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiStaticMixin) {
|
||||
class MixedClass extends baseClass implements ApiStaticMixin {
|
||||
public [_isStatic]: boolean;
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiStaticMixinOptions = args[0];
|
||||
this[_isStatic] = options.isStatic;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiStaticMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiStaticMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.isStatic = jsonObject.isStatic;
|
||||
}
|
||||
|
||||
public get isStatic(): boolean {
|
||||
return this[_isStatic];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiStaticMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.isStatic = this.isStatic;
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiStaticMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiStaticMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiStaticMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiStaticMixin {
|
||||
return apiItem.hasOwnProperty(_isStatic);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { InternalError } from '@rushstack/node-core-library';
|
||||
import { ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import type { ApiItem, IApiItemJson, IApiItemConstructor, IApiItemOptions } from '../items/ApiItem.js';
|
||||
import type { DeserializerContext } from '../model/DeserializerContext.js';
|
||||
import { TypeParameter } from '../model/TypeParameter.js';
|
||||
import type { Excerpt, IExcerptTokenRange } from './Excerpt.js';
|
||||
|
||||
/**
|
||||
* Represents parameter information that is part of {@link IApiTypeParameterListMixinOptions}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiTypeParameterOptions {
|
||||
constraintTokenRange: IExcerptTokenRange;
|
||||
defaultTypeTokenRange: IExcerptTokenRange;
|
||||
typeParameterName: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor options for {@link (ApiTypeParameterListMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiTypeParameterListMixinOptions extends IApiItemOptions {
|
||||
typeParameters: IApiTypeParameterOptions[];
|
||||
}
|
||||
|
||||
export interface IApiTypeParameterListMixinJson extends IApiItemJson {
|
||||
typeParameters: IApiTypeParameterOptions[];
|
||||
}
|
||||
|
||||
const _typeParameters: unique symbol = Symbol('ApiTypeParameterListMixin._typeParameters');
|
||||
|
||||
/**
|
||||
* The mixin base class for API items that can have type parameters.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations. The non-abstract classes (e.g. `ApiClass`, `ApiEnum`, `ApiInterface`, etc.) use
|
||||
* TypeScript "mixin" functions (e.g. `ApiDeclaredItem`, `ApiItemContainerMixin`, etc.) to add various
|
||||
* features that cannot be represented as a normal inheritance chain (since TypeScript does not allow a child class
|
||||
* to extend more than one base class). The "mixin" is a TypeScript merged declaration with three components:
|
||||
* the function that generates a subclass, an interface that describes the members of the subclass, and
|
||||
* a namespace containing static members of the class.
|
||||
* @public
|
||||
*/
|
||||
|
||||
export interface ApiTypeParameterListMixin extends ApiItem {
|
||||
serializeInto(jsonObject: Partial<IApiItemJson>): void;
|
||||
|
||||
/**
|
||||
* The type parameters.
|
||||
*/
|
||||
readonly typeParameters: readonly TypeParameter[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin function for {@link (ApiTypeParameterListMixin:interface)}.
|
||||
*
|
||||
* @param baseClass - The base class to be extended
|
||||
* @returns A child class that extends baseClass, adding the {@link (ApiTypeParameterListMixin:interface)}
|
||||
* functionality.
|
||||
* @public
|
||||
*/
|
||||
export function ApiTypeParameterListMixin<TBaseClass extends IApiItemConstructor>(
|
||||
baseClass: TBaseClass,
|
||||
): TBaseClass & (new (...args: any[]) => ApiTypeParameterListMixin) {
|
||||
class MixedClass extends baseClass implements ApiTypeParameterListMixin {
|
||||
public readonly [_typeParameters]: TypeParameter[];
|
||||
|
||||
public constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
const options: IApiTypeParameterListMixinOptions = args[0];
|
||||
|
||||
this[_typeParameters] = [];
|
||||
|
||||
if (this instanceof ApiDeclaredItem) {
|
||||
if (options.typeParameters) {
|
||||
for (const typeParameterOptions of options.typeParameters) {
|
||||
const defaultTypeExcerpt: Excerpt = this.buildExcerpt(typeParameterOptions.defaultTypeTokenRange);
|
||||
const typeParameter: TypeParameter = new TypeParameter({
|
||||
name: typeParameterOptions.typeParameterName,
|
||||
constraintExcerpt: this.buildExcerpt(typeParameterOptions.constraintTokenRange),
|
||||
defaultTypeExcerpt,
|
||||
isOptional: !defaultTypeExcerpt.isEmpty,
|
||||
parent: this,
|
||||
});
|
||||
|
||||
this[_typeParameters].push(typeParameter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new InternalError('ApiTypeParameterListMixin expects a base class that inherits from ApiDeclaredItem');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiTypeParameterListMixinOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiTypeParameterListMixinJson,
|
||||
): void {
|
||||
baseClass.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.typeParameters = jsonObject.typeParameters || [];
|
||||
}
|
||||
|
||||
public get typeParameters(): readonly TypeParameter[] {
|
||||
return this[_typeParameters];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiTypeParameterListMixinJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
const typeParameterObjects: IApiTypeParameterOptions[] = [];
|
||||
for (const typeParameter of this.typeParameters) {
|
||||
typeParameterObjects.push({
|
||||
typeParameterName: typeParameter.name,
|
||||
constraintTokenRange: typeParameter.constraintExcerpt.tokenRange,
|
||||
defaultTypeTokenRange: typeParameter.defaultTypeExcerpt.tokenRange,
|
||||
});
|
||||
}
|
||||
|
||||
if (typeParameterObjects.length > 0) {
|
||||
jsonObject.typeParameters = typeParameterObjects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return MixedClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Static members for {@link (ApiTypeParameterListMixin:interface)}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace ApiTypeParameterListMixin {
|
||||
/**
|
||||
* A type guard that tests whether the specified `ApiItem` subclass extends the `ApiParameterListMixin` mixin.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The JavaScript `instanceof` operator cannot be used to test for mixin inheritance, because each invocation of
|
||||
* the mixin function produces a different subclass. (This could be mitigated by `Symbol.hasInstance`, however
|
||||
* the TypeScript type system cannot invoke a runtime test.)
|
||||
*/
|
||||
export function isBaseClassOf(apiItem: ApiItem): apiItem is ApiTypeParameterListMixin {
|
||||
return apiItem.hasOwnProperty(_typeParameters);
|
||||
}
|
||||
}
|
||||
173
packages/api-extractor-model/src/mixins/Excerpt.ts
Normal file
173
packages/api-extractor-model/src/mixins/Excerpt.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference';
|
||||
import { Text } from '@rushstack/node-core-library';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export enum ExcerptTokenKind {
|
||||
/**
|
||||
* Generic text without any special properties
|
||||
*/
|
||||
Content = 'Content',
|
||||
|
||||
/**
|
||||
* A reference to an API declaration
|
||||
*/
|
||||
Reference = 'Reference',
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by {@link Excerpt} to indicate a range of indexes within an array of `ExcerptToken` objects.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IExcerptTokenRange {
|
||||
/**
|
||||
* The index of the last member of the span, plus one.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* If `startIndex` and `endIndex` are the same number, then the span is empty.
|
||||
*/
|
||||
endIndex: number;
|
||||
|
||||
/**
|
||||
* The starting index of the span.
|
||||
*/
|
||||
startIndex: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface IExcerptToken {
|
||||
canonicalReference?: string | undefined;
|
||||
readonly kind: ExcerptTokenKind;
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a fragment of text belonging to an {@link Excerpt} object.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class ExcerptToken {
|
||||
private readonly _kind: ExcerptTokenKind;
|
||||
|
||||
private readonly _text: string;
|
||||
|
||||
private readonly _canonicalReference: DeclarationReference | undefined;
|
||||
|
||||
public constructor(kind: ExcerptTokenKind, text: string, canonicalReference?: DeclarationReference) {
|
||||
this._kind = kind;
|
||||
|
||||
// Standardize the newlines across operating systems. Even though this may deviate from the actual
|
||||
// input source file that was parsed, it's useful because the newline gets serialized inside
|
||||
// a string literal in .api.json, which cannot be automatically normalized by Git.
|
||||
this._text = Text.convertToLf(text);
|
||||
this._canonicalReference = canonicalReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the kind of token.
|
||||
*/
|
||||
public get kind(): ExcerptTokenKind {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
/**
|
||||
* The text fragment.
|
||||
*/
|
||||
public get text(): string {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
/**
|
||||
* The hyperlink target for a token whose type is `ExcerptTokenKind.Reference`. For other token types,
|
||||
* this property will be `undefined`.
|
||||
*/
|
||||
public get canonicalReference(): DeclarationReference | undefined {
|
||||
return this._canonicalReference;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Excerpt` class is used by {@link ApiDeclaredItem} to represent a TypeScript code fragment that may be
|
||||
* annotated with hyperlinks to declared types (and in the future, source code locations).
|
||||
*
|
||||
* @remarks
|
||||
* API Extractor's .api.json file format stores excerpts compactly as a start/end indexes into an array of tokens.
|
||||
* Every `ApiDeclaredItem` has a "main excerpt" corresponding to the full list of tokens. The declaration may
|
||||
* also have have "captured" excerpts that correspond to subranges of tokens.
|
||||
*
|
||||
* For example, if the main excerpt is:
|
||||
*
|
||||
* ```
|
||||
* function parse(s: string): Vector | undefined;
|
||||
* ```
|
||||
*
|
||||
* ...then this entire signature is the "main excerpt", whereas the function's return type `Vector | undefined` is a
|
||||
* captured excerpt. The `Vector` token might be a hyperlink to that API item.
|
||||
*
|
||||
* An excerpt may be empty (i.e. a token range containing zero tokens). For example, if a function's return value
|
||||
* is not explicitly declared, then the returnTypeExcerpt will be empty. By contrast, a class constructor cannot
|
||||
* have a return value, so ApiConstructor has no returnTypeExcerpt property at all.
|
||||
* @public
|
||||
*/
|
||||
export class Excerpt {
|
||||
/**
|
||||
* The complete list of tokens for the source code fragment that this excerpt is based upon.
|
||||
* If this object is the main excerpt, then it will span all of the tokens; otherwise, it will correspond to
|
||||
* a range within the array.
|
||||
*/
|
||||
public readonly tokens: readonly ExcerptToken[];
|
||||
|
||||
/**
|
||||
* Specifies the excerpt's range within the `tokens` array.
|
||||
*/
|
||||
public readonly tokenRange: Readonly<IExcerptTokenRange>;
|
||||
|
||||
/**
|
||||
* The tokens spanned by this excerpt. It is the range of the `tokens` array as specified by the `tokenRange`
|
||||
* property.
|
||||
*/
|
||||
public readonly spannedTokens: readonly ExcerptToken[];
|
||||
|
||||
private _text: string | undefined;
|
||||
|
||||
public constructor(tokens: readonly ExcerptToken[], tokenRange: IExcerptTokenRange) {
|
||||
this.tokens = tokens;
|
||||
this.tokenRange = tokenRange;
|
||||
|
||||
if (
|
||||
this.tokenRange.startIndex < 0 ||
|
||||
this.tokenRange.endIndex > this.tokens.length ||
|
||||
this.tokenRange.startIndex > this.tokenRange.endIndex
|
||||
) {
|
||||
throw new Error('Invalid token range');
|
||||
}
|
||||
|
||||
this.spannedTokens = this.tokens.slice(this.tokenRange.startIndex, this.tokenRange.endIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* The excerpted text, formed by concatenating the text of the `spannedTokens` strings.
|
||||
*/
|
||||
public get text(): string {
|
||||
if (this._text === undefined) {
|
||||
this._text = this.spannedTokens.map((x) => x.text).join('');
|
||||
}
|
||||
|
||||
return this._text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the excerpt is an empty range.
|
||||
*/
|
||||
public get isEmpty(): boolean {
|
||||
return this.tokenRange.startIndex === this.tokenRange.endIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { ApiItem } from '../items/ApiItem.js';
|
||||
|
||||
/**
|
||||
* Generic result object for finding API items used by different kinds of find operations.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IFindApiItemsResult {
|
||||
/**
|
||||
* The API items that were found. Not guaranteed to be complete, see `maybeIncompleteResult`.
|
||||
*/
|
||||
items: ApiItem[];
|
||||
|
||||
/**
|
||||
* Indicates whether the result is potentially incomplete due to errors during the find operation.
|
||||
* If true, the `messages` explain the errors in more detail.
|
||||
*/
|
||||
maybeIncompleteResult: boolean;
|
||||
|
||||
/**
|
||||
* Diagnostic messages regarding the find operation.
|
||||
*/
|
||||
messages: IFindApiItemsMessage[];
|
||||
}
|
||||
|
||||
/**
|
||||
* This object is used for messages returned as part of `IFindApiItemsResult`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IFindApiItemsMessage {
|
||||
/**
|
||||
* Unique identifier for the message.
|
||||
*
|
||||
* @beta
|
||||
*/
|
||||
messageId: FindApiItemsMessageId;
|
||||
|
||||
/**
|
||||
* Text description of the message.
|
||||
*/
|
||||
text: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique identifiers for messages returned as part of `IFindApiItemsResult`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export enum FindApiItemsMessageId {
|
||||
/**
|
||||
* "Unable to resolve declaration reference within API item ___: ___"
|
||||
*/
|
||||
DeclarationResolutionFailed = 'declaration-resolution-failed',
|
||||
|
||||
/**
|
||||
* "Unable to analyze extends clause ___ of API item ___ because no canonical reference was found."
|
||||
*/
|
||||
ExtendsClauseMissingReference = 'extends-clause-missing-reference',
|
||||
|
||||
/**
|
||||
* "Unable to analyze references of API item ___ because it is not associated with an ApiModel"
|
||||
*/
|
||||
NoAssociatedApiModel = 'no-associated-api-model',
|
||||
|
||||
/**
|
||||
* "Unable to analyze references of API item ___ because it is of unsupported kind ___"
|
||||
*/
|
||||
UnsupportedKind = 'unsupported-kind',
|
||||
}
|
||||
18
packages/api-extractor-model/src/mixins/Mixin.ts
Normal file
18
packages/api-extractor-model/src/mixins/Mixin.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
/**
|
||||
* This abstraction is used by the mixin pattern.
|
||||
* It describes a class constructor.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* This abstraction is used by the mixin pattern.
|
||||
* It describes the "static side" of a class.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PropertiesOf<T> = { [K in keyof T]: T[K] };
|
||||
90
packages/api-extractor-model/src/model/ApiCallSignature.ts
Normal file
90
packages/api-extractor-model/src/model/ApiCallSignature.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin.js';
|
||||
import {
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
ApiTypeParameterListMixin,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiCallSignature}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiCallSignatureOptions
|
||||
extends IApiTypeParameterListMixinOptions,
|
||||
IApiParameterListMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReturnTypeMixinOptions,
|
||||
IApiDeclaredItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript function call signature.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiCallSignature` represents a TypeScript declaration such as `(x: number, y: number): number`
|
||||
* in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export interface IChooser {
|
||||
* // A call signature:
|
||||
* (x: number, y: number): number;
|
||||
*
|
||||
* // Another overload for this call signature:
|
||||
* (x: string, y: string): string;
|
||||
* }
|
||||
*
|
||||
* function chooseFirst<T>(x: T, y: T): T {
|
||||
* return x;
|
||||
* }
|
||||
*
|
||||
* let chooser: IChooser = chooseFirst;
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiCallSignature extends ApiTypeParameterListMixin(
|
||||
ApiParameterListMixin(ApiReleaseTagMixin(ApiReturnTypeMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
public constructor(options: IApiCallSignatureOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(overloadIndex: number): string {
|
||||
return `|${ApiItemKind.CallSignature}|${overloadIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.CallSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiCallSignature.getContainerKey(this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const parent: DeclarationReference = this.parent
|
||||
? this.parent.canonicalReference
|
||||
: // .withMeaning() requires some kind of component
|
||||
DeclarationReference.empty().addNavigationStep(Navigation.Members as any, '(parent)');
|
||||
return parent.withMeaning(Meaning.CallSignature as any).withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
157
packages/api-extractor-model/src/model/ApiClass.ts
Normal file
157
packages/api-extractor-model/src/model/ApiClass.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions, type IApiDeclaredItemJson } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import {
|
||||
ApiAbstractMixin,
|
||||
type IApiAbstractMixinJson,
|
||||
type IApiAbstractMixinOptions,
|
||||
} from '../mixins/ApiAbstractMixin.js';
|
||||
import {
|
||||
type IApiExportedMixinJson,
|
||||
type IApiExportedMixinOptions,
|
||||
ApiExportedMixin,
|
||||
} from '../mixins/ApiExportedMixin.js';
|
||||
import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import {
|
||||
ApiTypeParameterListMixin,
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
type IApiTypeParameterListMixinJson,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
import type { IExcerptTokenRange } from '../mixins/Excerpt.js';
|
||||
import type { DeserializerContext } from './DeserializerContext.js';
|
||||
import { HeritageType } from './HeritageType.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiClass}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiClassOptions
|
||||
extends IApiItemContainerMixinOptions,
|
||||
IApiNameMixinOptions,
|
||||
IApiAbstractMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiTypeParameterListMixinOptions,
|
||||
IApiExportedMixinOptions {
|
||||
extendsTokenRange: IExcerptTokenRange | undefined;
|
||||
implementsTokenRanges: IExcerptTokenRange[];
|
||||
}
|
||||
|
||||
export interface IApiClassJson
|
||||
extends IApiDeclaredItemJson,
|
||||
IApiAbstractMixinJson,
|
||||
IApiTypeParameterListMixinJson,
|
||||
IApiExportedMixinJson {
|
||||
extendsTokenRange?: IExcerptTokenRange;
|
||||
implementsTokenRanges: IExcerptTokenRange[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript class declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiClass` represents a TypeScript declaration such as this:
|
||||
*
|
||||
* ```ts
|
||||
* export class X { }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiClass extends ApiItemContainerMixin(
|
||||
ApiNameMixin(ApiAbstractMixin(ApiTypeParameterListMixin(ApiReleaseTagMixin(ApiExportedMixin(ApiDeclaredItem))))),
|
||||
) {
|
||||
/**
|
||||
* The base class that this class inherits from (using the `extends` keyword), or undefined if there is no base class.
|
||||
*/
|
||||
public readonly extendsType: HeritageType | undefined;
|
||||
|
||||
private readonly _implementsTypes: HeritageType[] = [];
|
||||
|
||||
public constructor(options: IApiClassOptions) {
|
||||
super(options);
|
||||
|
||||
if (options.extendsTokenRange) {
|
||||
this.extendsType = new HeritageType(this.buildExcerpt(options.extendsTokenRange));
|
||||
} else {
|
||||
this.extendsType = undefined;
|
||||
}
|
||||
|
||||
for (const implementsTokenRange of options.implementsTokenRanges) {
|
||||
this._implementsTypes.push(new HeritageType(this.buildExcerpt(implementsTokenRange)));
|
||||
}
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.Class}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiClassOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiClassJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.extendsTokenRange = jsonObject.extendsTokenRange;
|
||||
options.implementsTokenRanges = jsonObject.implementsTokenRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiClass.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of interfaces that this class implements using the `implements` keyword.
|
||||
*/
|
||||
public get implementsTypes(): readonly HeritageType[] {
|
||||
return this._implementsTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiClassJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
// Note that JSON does not support the "undefined" value, so we simply omit the field entirely if it is undefined
|
||||
if (this.extendsType) {
|
||||
jsonObject.extendsTokenRange = this.extendsType.excerpt.tokenRange;
|
||||
}
|
||||
|
||||
jsonObject.implementsTokenRanges = this.implementsTypes.map((x) => x.excerpt.tokenRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.Class as any);
|
||||
}
|
||||
}
|
||||
103
packages/api-extractor-model/src/model/ApiConstructSignature.ts
Normal file
103
packages/api-extractor-model/src/model/ApiConstructSignature.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin.js';
|
||||
import {
|
||||
ApiTypeParameterListMixin,
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiConstructor}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiConstructSignatureOptions
|
||||
extends IApiTypeParameterListMixinOptions,
|
||||
IApiParameterListMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReturnTypeMixinOptions,
|
||||
IApiDeclaredItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript construct signature that belongs to an `ApiInterface`.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiConstructSignature` represents a construct signature using the `new` keyword such as in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export interface IVector {
|
||||
* x: number;
|
||||
* y: number;
|
||||
* }
|
||||
*
|
||||
* export interface IVectorConstructor {
|
||||
* // A construct signature:
|
||||
* new(x: number, y: number): IVector;
|
||||
* }
|
||||
*
|
||||
* export function createVector(vectorConstructor: IVectorConstructor,
|
||||
* x: number, y: number): IVector {
|
||||
* return new vectorConstructor(x, y);
|
||||
* }
|
||||
*
|
||||
* class Vector implements IVector {
|
||||
* public x: number;
|
||||
* public y: number;
|
||||
* public constructor(x: number, y: number) {
|
||||
* this.x = x;
|
||||
* this.y = y;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* let vector: Vector = createVector(Vector, 1, 2);
|
||||
* ```
|
||||
*
|
||||
* Compare with {@link ApiConstructor}, which describes the class constructor itself.
|
||||
* @public
|
||||
*/
|
||||
export class ApiConstructSignature extends ApiTypeParameterListMixin(
|
||||
ApiParameterListMixin(ApiReleaseTagMixin(ApiReturnTypeMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
public constructor(options: IApiConstructSignatureOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(overloadIndex: number): string {
|
||||
return `|${ApiItemKind.ConstructSignature}|${overloadIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.ConstructSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiConstructSignature.getContainerKey(this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const parent: DeclarationReference = this.parent
|
||||
? this.parent.canonicalReference
|
||||
: // .withMeaning() requires some kind of component
|
||||
DeclarationReference.empty().addNavigationStep(Navigation.Members as any, '(parent)');
|
||||
return parent.withMeaning(Meaning.ConstructSignature as any).withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
81
packages/api-extractor-model/src/model/ApiConstructor.ts
Normal file
81
packages/api-extractor-model/src/model/ApiConstructor.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import { ApiProtectedMixin, type IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin.js';
|
||||
import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiConstructor}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiConstructorOptions
|
||||
extends IApiParameterListMixinOptions,
|
||||
IApiProtectedMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript class constructor declaration that belongs to an `ApiClass`.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiConstructor` represents a declaration using the `constructor` keyword such as in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export class Vector {
|
||||
* public x: number;
|
||||
* public y: number;
|
||||
*
|
||||
* // A class constructor:
|
||||
* public constructor(x: number, y: number) {
|
||||
* this.x = x;
|
||||
* this.y = y;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Compare with {@link ApiConstructSignature}, which describes the construct signature for a class constructor.
|
||||
* @public
|
||||
*/
|
||||
export class ApiConstructor extends ApiParameterListMixin(ApiProtectedMixin(ApiReleaseTagMixin(ApiDeclaredItem))) {
|
||||
public constructor(options: IApiConstructorOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(overloadIndex: number): string {
|
||||
return `|${ApiItemKind.Constructor}|${overloadIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Constructor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiConstructor.getContainerKey(this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const parent: DeclarationReference = this.parent
|
||||
? this.parent.canonicalReference
|
||||
: // .withMeaning() requires some kind of component
|
||||
DeclarationReference.empty().addNavigationStep(Navigation.Members as any, '(parent)');
|
||||
return parent.withMeaning(Meaning.Constructor as any).withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
89
packages/api-extractor-model/src/model/ApiEntryPoint.ts
Normal file
89
packages/api-extractor-model/src/model/ApiEntryPoint.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiItem, ApiItemKind } from '../items/ApiItem.js';
|
||||
import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiPackage } from './ApiPackage.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiEntryPoint}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiEntryPointOptions extends IApiItemContainerMixinOptions, IApiNameMixinOptions {}
|
||||
|
||||
/**
|
||||
* Represents the entry point for an NPM package.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiEntryPoint` represents the entry point to an NPM package. API Extractor does not currently support
|
||||
* analysis of multiple entry points, but the `ApiEntryPoint` object is included to support a future feature.
|
||||
* In the current implementation, `ApiEntryPoint.importPath` is always the empty string.
|
||||
*
|
||||
* For example, suppose the package.json file looks like this:
|
||||
*
|
||||
* ```json
|
||||
* {
|
||||
* "name": "example-library",
|
||||
* "version": "1.0.0",
|
||||
* "main": "./lib/index.js",
|
||||
* "typings": "./lib/index.d.ts"
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* In this example, the `ApiEntryPoint` would represent the TypeScript module for `./lib/index.js`.
|
||||
* @public
|
||||
*/
|
||||
export class ApiEntryPoint extends ApiItemContainerMixin(ApiNameMixin(ApiItem)) {
|
||||
public constructor(options: IApiEntryPointOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.EntryPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
// No prefix needed, because ApiEntryPoint is the only possible member of an ApiPackage
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module path for this entry point, relative to the parent `ApiPackage`. In the current implementation,
|
||||
* this is always the empty string, indicating the default entry point.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* API Extractor does not currently support analysis of multiple entry points. If that feature is implemented
|
||||
* in the future, then the `ApiEntryPoint.importPath` will be used to distinguish different entry points,
|
||||
* for example: `controls/Button` in `import { Button } from "example-package/controls/Button";`.
|
||||
*
|
||||
* The `ApiEntryPoint.name` property stores the same value as `ApiEntryPoint.importPath`.
|
||||
*/
|
||||
public get importPath(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
if (this.parent instanceof ApiPackage) {
|
||||
return DeclarationReference.package(this.parent.name, this.importPath);
|
||||
}
|
||||
|
||||
return DeclarationReference.empty();
|
||||
}
|
||||
}
|
||||
97
packages/api-extractor-model/src/model/ApiEnum.ts
Normal file
97
packages/api-extractor-model/src/model/ApiEnum.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin.js';
|
||||
import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import type { ApiEnumMember } from './ApiEnumMember.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiEnum}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiEnumOptions
|
||||
extends IApiItemContainerMixinOptions,
|
||||
IApiNameMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiExportedMixinOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript enum declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiEnum` represents an enum declaration such as `FontSizes` in the example below:
|
||||
*
|
||||
* ```ts
|
||||
* export enum FontSizes {
|
||||
* Small = 100,
|
||||
* Medium = 200,
|
||||
* Large = 300
|
||||
* }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiEnum extends ApiItemContainerMixin(
|
||||
ApiNameMixin(ApiReleaseTagMixin(ApiExportedMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
public constructor(options: IApiEnumOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.Enum}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Enum;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get members(): readonly ApiEnumMember[] {
|
||||
return super.members as readonly ApiEnumMember[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiEnum.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override addMember(member: ApiEnumMember): void {
|
||||
if (member.kind !== ApiItemKind.EnumMember) {
|
||||
throw new Error('Only ApiEnumMember objects can be added to an ApiEnum');
|
||||
}
|
||||
|
||||
super.addMember(member);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.Enum as any);
|
||||
}
|
||||
}
|
||||
101
packages/api-extractor-model/src/model/ApiEnumMember.ts
Normal file
101
packages/api-extractor-model/src/model/ApiEnumMember.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { ApiInitializerMixin, type IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiEnumMember}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiEnumMemberOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiInitializerMixinOptions {}
|
||||
|
||||
/**
|
||||
* Options for customizing the sort order of {@link ApiEnum} members.
|
||||
*
|
||||
* @privateRemarks
|
||||
* This enum is currently only used by the `@microsoft/api-extractor` package; it is declared here
|
||||
* because we anticipate that if more options are added in the future, their sorting will be implemented
|
||||
* by the `@microsoft/api-extractor-model` package.
|
||||
*
|
||||
* See https://github.com/microsoft/rushstack/issues/918 for details.
|
||||
* @public
|
||||
*/
|
||||
export enum EnumMemberOrder {
|
||||
/**
|
||||
* `ApiEnumMember` items are sorted according to their {@link ApiItem.getSortKey}. The order is
|
||||
* basically alphabetical by identifier name, but otherwise unspecified to allow for cosmetic improvements.
|
||||
*
|
||||
* This is the default behavior.
|
||||
*/
|
||||
ByName = 'by-name',
|
||||
|
||||
/**
|
||||
* `ApiEnumMember` items preserve the original order of the declarations in the source file.
|
||||
* (This disables the automatic sorting that is normally applied based on {@link ApiItem.getSortKey}.)
|
||||
*/
|
||||
Preserve = 'preserve',
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a member of a TypeScript enum declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiEnumMember` represents an enum member such as `Small = 100` in the example below:
|
||||
*
|
||||
* ```ts
|
||||
* export enum FontSizes {
|
||||
* Small = 100,
|
||||
* Medium = 200,
|
||||
* Large = 300
|
||||
* }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiEnumMember extends ApiNameMixin(ApiReleaseTagMixin(ApiInitializerMixin(ApiDeclaredItem))) {
|
||||
public constructor(options: IApiEnumMemberOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
// No prefix needed, because ApiEnumMember is the only possible member of an ApiEnum
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.EnumMember;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiEnumMember.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(Navigation.Exports as any, nameComponent)
|
||||
.withMeaning(Meaning.Member as any);
|
||||
}
|
||||
}
|
||||
89
packages/api-extractor-model/src/model/ApiFunction.ts
Normal file
89
packages/api-extractor-model/src/model/ApiFunction.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin.js';
|
||||
import {
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
ApiTypeParameterListMixin,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiFunction}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiFunctionOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiTypeParameterListMixinOptions,
|
||||
IApiParameterListMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReturnTypeMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiExportedMixinOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript function declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiFunction` represents a TypeScript declaration such as this example:
|
||||
*
|
||||
* ```ts
|
||||
* export function getAverage(x: number, y: number): number {
|
||||
* return (x + y) / 2.0;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Functions are exported by an entry point module or by a namespace. Compare with {@link ApiMethod}, which
|
||||
* represents a function that is a member of a class.
|
||||
* @public
|
||||
*/
|
||||
export class ApiFunction extends ApiNameMixin(
|
||||
ApiTypeParameterListMixin(
|
||||
ApiParameterListMixin(ApiReleaseTagMixin(ApiReturnTypeMixin(ApiExportedMixin(ApiDeclaredItem)))),
|
||||
),
|
||||
) {
|
||||
public constructor(options: IApiFunctionOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string, overloadIndex: number): string {
|
||||
return `${name}|${ApiItemKind.Function}|${overloadIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Function;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiFunction.getContainerKey(this.name, this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.Function as any)
|
||||
.withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
80
packages/api-extractor-model/src/model/ApiIndexSignature.ts
Normal file
80
packages/api-extractor-model/src/model/ApiIndexSignature.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import { type IApiReadonlyMixinOptions, ApiReadonlyMixin } from '../mixins/ApiReadonlyMixin.js';
|
||||
import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiIndexSignature}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiIndexSignatureOptions
|
||||
extends IApiParameterListMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReturnTypeMixinOptions,
|
||||
IApiReadonlyMixinOptions,
|
||||
IApiDeclaredItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript index signature.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiIndexSignature` represents a TypeScript declaration such as `[x: number]: number` in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export interface INumberTable {
|
||||
* // An index signature
|
||||
* [value: number]: number;
|
||||
*
|
||||
* // An overloaded index signature
|
||||
* [name: string]: number;
|
||||
* }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiIndexSignature extends ApiParameterListMixin(
|
||||
ApiReleaseTagMixin(ApiReturnTypeMixin(ApiReadonlyMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
public constructor(options: IApiIndexSignatureOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(overloadIndex: number): string {
|
||||
return `|${ApiItemKind.IndexSignature}|${overloadIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.IndexSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiIndexSignature.getContainerKey(this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const parent: DeclarationReference = this.parent
|
||||
? this.parent.canonicalReference
|
||||
: // .withMeaning() requires some kind of component
|
||||
DeclarationReference.empty().addNavigationStep(Navigation.Members as any, '(parent)');
|
||||
return parent.withMeaning(Meaning.IndexSignature as any).withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
143
packages/api-extractor-model/src/model/ApiInterface.ts
Normal file
143
packages/api-extractor-model/src/model/ApiInterface.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions, type IApiDeclaredItemJson } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import {
|
||||
type IApiExportedMixinJson,
|
||||
type IApiExportedMixinOptions,
|
||||
ApiExportedMixin,
|
||||
} from '../mixins/ApiExportedMixin.js';
|
||||
import {
|
||||
ApiItemContainerMixin,
|
||||
type IApiItemContainerMixinOptions,
|
||||
type IApiItemContainerJson,
|
||||
} from '../mixins/ApiItemContainerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin, type IApiNameMixinJson } from '../mixins/ApiNameMixin.js';
|
||||
import {
|
||||
type IApiReleaseTagMixinOptions,
|
||||
ApiReleaseTagMixin,
|
||||
type IApiReleaseTagMixinJson,
|
||||
} from '../mixins/ApiReleaseTagMixin.js';
|
||||
import {
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
type IApiTypeParameterListMixinJson,
|
||||
ApiTypeParameterListMixin,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
import type { IExcerptTokenRange } from '../mixins/Excerpt.js';
|
||||
import type { DeserializerContext } from './DeserializerContext.js';
|
||||
import { HeritageType } from './HeritageType.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiInterface}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiInterfaceOptions
|
||||
extends IApiItemContainerMixinOptions,
|
||||
IApiNameMixinOptions,
|
||||
IApiTypeParameterListMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiExportedMixinOptions {
|
||||
extendsTokenRanges: IExcerptTokenRange[];
|
||||
}
|
||||
|
||||
export interface IApiInterfaceJson
|
||||
extends IApiItemContainerJson,
|
||||
IApiNameMixinJson,
|
||||
IApiTypeParameterListMixinJson,
|
||||
IApiReleaseTagMixinJson,
|
||||
IApiDeclaredItemJson,
|
||||
IApiExportedMixinJson {
|
||||
extendsTokenRanges: IExcerptTokenRange[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript class declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiInterface` represents a TypeScript declaration such as this:
|
||||
*
|
||||
* ```ts
|
||||
* export interface X extends Y {
|
||||
* }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiInterface extends ApiItemContainerMixin(
|
||||
ApiNameMixin(ApiTypeParameterListMixin(ApiReleaseTagMixin(ApiExportedMixin(ApiDeclaredItem)))),
|
||||
) {
|
||||
private readonly _extendsTypes: HeritageType[] = [];
|
||||
|
||||
public constructor(options: IApiInterfaceOptions) {
|
||||
super(options);
|
||||
|
||||
for (const extendsTokenRange of options.extendsTokenRanges) {
|
||||
this._extendsTypes.push(new HeritageType(this.buildExcerpt(extendsTokenRange)));
|
||||
}
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.Interface}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiInterfaceOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiInterfaceJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.extendsTokenRanges = jsonObject.extendsTokenRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Interface;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiInterface.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of base interfaces that this interface inherits from using the `extends` keyword.
|
||||
*/
|
||||
public get extendsTypes(): readonly HeritageType[] {
|
||||
return this._extendsTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiInterfaceJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.extendsTokenRanges = this.extendsTypes.map((x) => x.excerpt.tokenRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.Interface as any);
|
||||
}
|
||||
}
|
||||
104
packages/api-extractor-model/src/model/ApiMethod.ts
Normal file
104
packages/api-extractor-model/src/model/ApiMethod.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiAbstractMixinOptions, ApiAbstractMixin } from '../mixins/ApiAbstractMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiOptionalMixin, type IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin.js';
|
||||
import { type IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import { ApiProtectedMixin, type IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin.js';
|
||||
import { type IApiReleaseTagMixinOptions, ApiReleaseTagMixin } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import { ApiReturnTypeMixin, type IApiReturnTypeMixinOptions } from '../mixins/ApiReturnTypeMixin.js';
|
||||
import { ApiStaticMixin, type IApiStaticMixinOptions } from '../mixins/ApiStaticMixin.js';
|
||||
import {
|
||||
ApiTypeParameterListMixin,
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiMethod}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiMethodOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiAbstractMixinOptions,
|
||||
IApiOptionalMixinOptions,
|
||||
IApiParameterListMixinOptions,
|
||||
IApiProtectedMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReturnTypeMixinOptions,
|
||||
IApiStaticMixinOptions,
|
||||
IApiTypeParameterListMixinOptions,
|
||||
IApiDeclaredItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript member function declaration that belongs to an `ApiClass`.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiMethod` represents a TypeScript declaration such as the `render` member function in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export class Widget {
|
||||
* public render(): void { }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Compare with {@link ApiMethodSignature}, which represents a method belonging to an interface.
|
||||
* For example, a class method can be `static` but an interface method cannot.
|
||||
* @public
|
||||
*/
|
||||
export class ApiMethod extends ApiNameMixin(
|
||||
ApiAbstractMixin(
|
||||
ApiOptionalMixin(
|
||||
ApiParameterListMixin(
|
||||
ApiProtectedMixin(
|
||||
ApiReleaseTagMixin(ApiReturnTypeMixin(ApiStaticMixin(ApiTypeParameterListMixin(ApiDeclaredItem)))),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
) {
|
||||
public constructor(options: IApiMethodOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string, isStatic: boolean, overloadIndex: number): string {
|
||||
if (isStatic) {
|
||||
return `${name}|${ApiItemKind.Method}|static|${overloadIndex}`;
|
||||
} else {
|
||||
return `${name}|${ApiItemKind.Method}|instance|${overloadIndex}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Method;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiMethod.getContainerKey(this.name, this.isStatic, this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep((this.isStatic ? Navigation.Exports : Navigation.Members) as any, nameComponent)
|
||||
.withMeaning(Meaning.Member as any)
|
||||
.withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
86
packages/api-extractor-model/src/model/ApiMethodSignature.ts
Normal file
86
packages/api-extractor-model/src/model/ApiMethodSignature.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiOptionalMixin, type IApiOptionalMixinOptions } from '../mixins/ApiOptionalMixin.js';
|
||||
import { ApiParameterListMixin, type IApiParameterListMixinOptions } from '../mixins/ApiParameterListMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import { type IApiReturnTypeMixinOptions, ApiReturnTypeMixin } from '../mixins/ApiReturnTypeMixin.js';
|
||||
import {
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
ApiTypeParameterListMixin,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface IApiMethodSignatureOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiTypeParameterListMixinOptions,
|
||||
IApiParameterListMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReturnTypeMixinOptions,
|
||||
IApiOptionalMixinOptions,
|
||||
IApiDeclaredItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript member function declaration that belongs to an `ApiInterface`.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiMethodSignature` represents a TypeScript declaration such as the `render` member function in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export interface IWidget {
|
||||
* render(): void;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Compare with {@link ApiMethod}, which represents a method belonging to a class.
|
||||
* For example, a class method can be `static` but an interface method cannot.
|
||||
* @public
|
||||
*/
|
||||
export class ApiMethodSignature extends ApiNameMixin(
|
||||
ApiTypeParameterListMixin(
|
||||
ApiParameterListMixin(ApiReleaseTagMixin(ApiReturnTypeMixin(ApiOptionalMixin(ApiDeclaredItem)))),
|
||||
),
|
||||
) {
|
||||
public constructor(options: IApiMethodSignatureOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string, overloadIndex: number): string {
|
||||
return `${name}|${ApiItemKind.MethodSignature}|${overloadIndex}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.MethodSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiMethodSignature.getContainerKey(this.name, this.overloadIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(Navigation.Members as any, nameComponent)
|
||||
.withMeaning(Meaning.Member as any)
|
||||
.withOverloadIndex(this.overloadIndex);
|
||||
}
|
||||
}
|
||||
207
packages/api-extractor-model/src/model/ApiModel.ts
Normal file
207
packages/api-extractor-model/src/model/ApiModel.ts
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DocDeclarationReference } from '@microsoft/tsdoc';
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { PackageName } from '@rushstack/node-core-library';
|
||||
import { ApiItem, ApiItemKind } from '../items/ApiItem.js';
|
||||
import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { ApiPackage } from './ApiPackage.js';
|
||||
import { ModelReferenceResolver, type IResolveDeclarationReferenceResult } from './ModelReferenceResolver.js';
|
||||
|
||||
/**
|
||||
* A serializable representation of a collection of API declarations.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* An `ApiModel` represents a collection of API declarations that can be serialized to disk. It captures all the
|
||||
* important information needed to generate documentation, without any reliance on the TypeScript compiler engine.
|
||||
*
|
||||
* An `ApiModel` acts as the root of a tree of objects that all inherit from the `ApiItem` base class.
|
||||
* The tree children are determined by the {@link (ApiItemContainerMixin:interface)} mixin base class. The model
|
||||
* contains packages. Packages have an entry point (today, only one). And the entry point can contain various types
|
||||
* of API declarations. The container relationships might look like this:
|
||||
*
|
||||
* ```
|
||||
* Things that can contain other things:
|
||||
*
|
||||
* - ApiModel
|
||||
* - ApiPackage
|
||||
* - ApiEntryPoint
|
||||
* - ApiClass
|
||||
* - ApiMethod
|
||||
* - ApiProperty
|
||||
* - ApiEnum
|
||||
* - ApiEnumMember
|
||||
* - ApiInterface
|
||||
* - ApiMethodSignature
|
||||
* - ApiPropertySignature
|
||||
* - ApiNamespace
|
||||
* - (ApiClass, ApiEnum, ApiInterace, ...)
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* Normally, API Extractor writes an .api.json file to disk for each project that it builds. Then, a tool like
|
||||
* API Documenter can load the various `ApiPackage` objects into a single `ApiModel` and process them as a group.
|
||||
* This is useful because compilation generally occurs separately (e.g. because projects may reside in different
|
||||
* Git repos, or because they build with different TypeScript compiler configurations that may be incompatible),
|
||||
* whereas API Documenter cannot detect broken hyperlinks without seeing the entire documentation set.
|
||||
* @public
|
||||
*/
|
||||
export class ApiModel extends ApiItemContainerMixin(ApiItem) {
|
||||
private readonly _resolver: ModelReferenceResolver;
|
||||
|
||||
private _packagesByName: Map<string, ApiPackage> | undefined = undefined;
|
||||
|
||||
private _apiItemsByCanonicalReference: Map<string, ApiItem> | undefined = undefined;
|
||||
|
||||
public constructor() {
|
||||
super({});
|
||||
|
||||
this._resolver = new ModelReferenceResolver(this);
|
||||
}
|
||||
|
||||
public loadPackage(apiJsonFilename: string): ApiPackage {
|
||||
const apiPackage: ApiPackage = ApiPackage.loadFromJsonFile(apiJsonFilename);
|
||||
this.addMember(apiPackage);
|
||||
return apiPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/class-literal-property-style
|
||||
public override get containerKey(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public get packages(): readonly ApiPackage[] {
|
||||
return this.members as readonly ApiPackage[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override addMember(member: ApiPackage): void {
|
||||
if (member.kind !== ApiItemKind.Package) {
|
||||
throw new Error('Only items of type ApiPackage may be added to an ApiModel');
|
||||
}
|
||||
|
||||
super.addMember(member);
|
||||
this._packagesByName = undefined; // invalidate the cache
|
||||
this._apiItemsByCanonicalReference = undefined; // invalidate the cache
|
||||
}
|
||||
|
||||
/**
|
||||
* Efficiently finds a package by the NPM package name.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* If the NPM scope is omitted in the package name, it will still be found provided that it is an unambiguous match.
|
||||
* For example, it's often convenient to write `{@link node-core-library#JsonFile}` instead of
|
||||
* `{@link @rushstack/node-core-library#JsonFile}`.
|
||||
*/
|
||||
public tryGetPackageByName(packageName: string): ApiPackage | undefined {
|
||||
// Build the lookup on demand
|
||||
if (this._packagesByName === undefined) {
|
||||
this._packagesByName = new Map<string, ApiPackage>();
|
||||
|
||||
const unscopedMap: Map<string, ApiPackage | undefined> = new Map<string, ApiPackage | undefined>();
|
||||
|
||||
for (const apiPackage of this.packages) {
|
||||
if (this._packagesByName.get(apiPackage.name)) {
|
||||
// This should not happen
|
||||
throw new Error(`The model contains multiple packages with the name ${apiPackage.name}`);
|
||||
}
|
||||
|
||||
this._packagesByName.set(apiPackage.name, apiPackage);
|
||||
|
||||
const unscopedName: string = PackageName.parse(apiPackage.name).unscopedName;
|
||||
|
||||
if (unscopedMap.has(unscopedName)) {
|
||||
// If another package has the same unscoped name, then we won't register it
|
||||
unscopedMap.set(unscopedName, undefined);
|
||||
} else {
|
||||
unscopedMap.set(unscopedName, apiPackage);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [unscopedName, apiPackage] of unscopedMap) {
|
||||
if (apiPackage && !this._packagesByName.has(unscopedName)) {
|
||||
// If the unscoped name is unambiguous, then we can also use it as a lookup
|
||||
this._packagesByName.set(unscopedName, apiPackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this._packagesByName.get(packageName);
|
||||
}
|
||||
|
||||
public resolveDeclarationReference(
|
||||
declarationReference: DeclarationReference | DocDeclarationReference,
|
||||
contextApiItem: ApiItem | undefined,
|
||||
): IResolveDeclarationReferenceResult {
|
||||
if (declarationReference instanceof DocDeclarationReference) {
|
||||
return this._resolver.resolve(declarationReference, contextApiItem);
|
||||
} else if (declarationReference instanceof DeclarationReference) {
|
||||
// use this._apiItemsByCanonicalReference to look up ApiItem
|
||||
|
||||
// Build the lookup on demand
|
||||
if (!this._apiItemsByCanonicalReference) {
|
||||
this._apiItemsByCanonicalReference = new Map<string, ApiItem>();
|
||||
|
||||
for (const apiPackage of this.packages) {
|
||||
this._initApiItemsRecursive(apiPackage, this._apiItemsByCanonicalReference);
|
||||
}
|
||||
}
|
||||
|
||||
const result: IResolveDeclarationReferenceResult = {
|
||||
resolvedApiItem: undefined,
|
||||
errorMessage: undefined,
|
||||
};
|
||||
|
||||
const apiItem: ApiItem | undefined = this._apiItemsByCanonicalReference.get(declarationReference.toString());
|
||||
|
||||
if (apiItem) {
|
||||
result.resolvedApiItem = apiItem;
|
||||
} else {
|
||||
result.errorMessage = `${declarationReference.toString()} can not be located`;
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
// NOTE: The "instanceof DeclarationReference" test assumes a specific version of the @microsoft/tsdoc package.
|
||||
throw new TypeError(
|
||||
'The "declarationReference" parameter must be an instance of' +
|
||||
' DocDeclarationReference or DeclarationReference',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private _initApiItemsRecursive(apiItem: ApiItem, apiItemsByCanonicalReference: Map<string, ApiItem>): void {
|
||||
if (apiItem.canonicalReference && !apiItem.canonicalReference.isEmpty) {
|
||||
apiItemsByCanonicalReference.set(apiItem.canonicalReference.toString(), apiItem);
|
||||
}
|
||||
|
||||
// Recurse container members
|
||||
if (ApiItemContainerMixin.isBaseClassOf(apiItem)) {
|
||||
for (const apiMember of apiItem.members) {
|
||||
this._initApiItemsRecursive(apiMember, apiItemsByCanonicalReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
return DeclarationReference.empty();
|
||||
}
|
||||
}
|
||||
80
packages/api-extractor-model/src/model/ApiNamespace.ts
Normal file
80
packages/api-extractor-model/src/model/ApiNamespace.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { type IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { type IApiExportedMixinOptions, ApiExportedMixin } from '../mixins/ApiExportedMixin.js';
|
||||
import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiClass}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiNamespaceOptions
|
||||
extends IApiItemContainerMixinOptions,
|
||||
IApiNameMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiExportedMixinOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript namespace declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiNamespace` represents a TypeScript declaration such `X` or `Y` in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export namespace X {
|
||||
* export namespace Y {
|
||||
* export interface IWidget {
|
||||
* render(): void;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiNamespace extends ApiItemContainerMixin(
|
||||
ApiNameMixin(ApiReleaseTagMixin(ApiExportedMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
public constructor(options: IApiNamespaceOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.Namespace}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Namespace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiNamespace.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.Namespace as any);
|
||||
}
|
||||
}
|
||||
312
packages/api-extractor-model/src/model/ApiPackage.ts
Normal file
312
packages/api-extractor-model/src/model/ApiPackage.ts
Normal file
@@ -0,0 +1,312 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { TSDocConfiguration } from '@microsoft/tsdoc';
|
||||
import { DeclarationReference } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { TSDocConfigFile } from '@microsoft/tsdoc-config';
|
||||
import {
|
||||
JsonFile,
|
||||
type IJsonFileSaveOptions,
|
||||
PackageJsonLookup,
|
||||
type IPackageJson,
|
||||
type JsonObject,
|
||||
} from '@rushstack/node-core-library';
|
||||
import { ApiDocumentedItem, type IApiDocumentedItemOptions } from '../items/ApiDocumentedItem.js';
|
||||
import { ApiItem, ApiItemKind, type IApiItemJson } from '../items/ApiItem.js';
|
||||
import { ApiItemContainerMixin, type IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import type { ApiEntryPoint } from './ApiEntryPoint.js';
|
||||
import { DeserializerContext, ApiJsonSchemaVersion } from './DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiPackage}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiPackageOptions
|
||||
extends IApiItemContainerMixinOptions,
|
||||
IApiNameMixinOptions,
|
||||
IApiDocumentedItemOptions {
|
||||
projectFolderUrl?: string | undefined;
|
||||
tsdocConfiguration: TSDocConfiguration;
|
||||
}
|
||||
|
||||
export interface IApiPackageMetadataJson {
|
||||
/**
|
||||
* To support forwards compatibility, the `oldestForwardsCompatibleVersion` field tracks the oldest schema version
|
||||
* whose corresponding deserializer could safely load this file.
|
||||
*
|
||||
* @remarks
|
||||
* Normally api-extractor-model should refuse to load a schema version that is newer than the latest version
|
||||
* that its deserializer understands. However, sometimes a schema change may merely introduce some new fields
|
||||
* without modifying or removing any existing fields. In this case, an older api-extractor-model library can
|
||||
* safely deserialize the newer version (by ignoring the extra fields that it doesn't recognize). The newer
|
||||
* serializer can use this field to communicate that.
|
||||
*
|
||||
* If present, the `oldestForwardsCompatibleVersion` must be less than or equal to
|
||||
* `IApiPackageMetadataJson.schemaVersion`.
|
||||
*/
|
||||
oldestForwardsCompatibleVersion?: ApiJsonSchemaVersion;
|
||||
|
||||
/**
|
||||
* The schema version for the .api.json file format. Used for determining whether the file format is
|
||||
* supported, and for backwards compatibility.
|
||||
*/
|
||||
schemaVersion: ApiJsonSchemaVersion;
|
||||
|
||||
/**
|
||||
* The NPM package name for the tool that wrote the *.api.json file.
|
||||
* For informational purposes only.
|
||||
*/
|
||||
toolPackage: string;
|
||||
|
||||
/**
|
||||
* The NPM package version for the tool that wrote the *.api.json file.
|
||||
* For informational purposes only.
|
||||
*/
|
||||
toolVersion: string;
|
||||
|
||||
/**
|
||||
* The TSDoc configuration that was used when analyzing the API for this package.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The structure of this objet is defined by the `@microsoft/tsdoc-config` library.
|
||||
* Normally this configuration is loaded from the project's tsdoc.json file. It is stored
|
||||
* in the .api.json file so that doc comments can be parsed accurately when loading the file.
|
||||
*/
|
||||
tsdocConfig: JsonObject;
|
||||
}
|
||||
|
||||
export interface IApiPackageJson extends IApiItemJson {
|
||||
/**
|
||||
* A file header that stores metadata about the tool that wrote the *.api.json file.
|
||||
*/
|
||||
metadata: IApiPackageMetadataJson;
|
||||
|
||||
/**
|
||||
* 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. Provided via the
|
||||
* `api-extractor.json` config.
|
||||
*/
|
||||
projectFolderUrl?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@link ApiPackage.saveToJsonFile}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiPackageSaveOptions extends IJsonFileSaveOptions {
|
||||
/**
|
||||
* Set to true only when invoking API Extractor's test harness.
|
||||
*
|
||||
* @remarks
|
||||
* When `testMode` is true, the `toolVersion` field in the .api.json file is assigned an empty string
|
||||
* to prevent spurious diffs in output files tracked for tests.
|
||||
*/
|
||||
testMode?: boolean;
|
||||
|
||||
/**
|
||||
* Optionally specifies a value for the "toolPackage" field in the output .api.json data file;
|
||||
* otherwise, the value will be "api-extractor-model".
|
||||
*/
|
||||
toolPackage?: string;
|
||||
|
||||
/**
|
||||
* Optionally specifies a value for the "toolVersion" field in the output .api.json data file;
|
||||
* otherwise, the value will be the current version of the api-extractor-model package.
|
||||
*/
|
||||
toolVersion?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an NPM package containing API declarations.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
* @public
|
||||
*/
|
||||
export class ApiPackage extends ApiItemContainerMixin(ApiNameMixin(ApiDocumentedItem)) {
|
||||
private readonly _tsdocConfiguration: TSDocConfiguration;
|
||||
|
||||
private readonly _projectFolderUrl?: string | undefined;
|
||||
|
||||
public constructor(options: IApiPackageOptions) {
|
||||
super(options);
|
||||
|
||||
this._tsdocConfiguration = options.tsdocConfiguration;
|
||||
this._projectFolderUrl = options.projectFolderUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiPackageOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiPackageJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.projectFolderUrl = jsonObject.projectFolderUrl;
|
||||
}
|
||||
|
||||
public static loadFromJsonFile(apiJsonFilename: string): ApiPackage {
|
||||
const jsonObject: IApiPackageJson = JsonFile.load(apiJsonFilename);
|
||||
|
||||
if (!jsonObject?.metadata || typeof jsonObject.metadata.schemaVersion !== 'number') {
|
||||
throw new Error(
|
||||
`Error loading ${apiJsonFilename}:` +
|
||||
`\nThe file format is not recognized; the "metadata.schemaVersion" field is missing or invalid`,
|
||||
);
|
||||
}
|
||||
|
||||
const schemaVersion: number = jsonObject.metadata.schemaVersion;
|
||||
|
||||
if (schemaVersion < ApiJsonSchemaVersion.OLDEST_SUPPORTED) {
|
||||
throw new Error(
|
||||
`Error loading ${apiJsonFilename}:` +
|
||||
`\nThe file format is version ${schemaVersion},` +
|
||||
` whereas ${ApiJsonSchemaVersion.OLDEST_SUPPORTED} is the oldest version supported by this tool`,
|
||||
);
|
||||
}
|
||||
|
||||
let oldestForwardsCompatibleVersion: number = schemaVersion;
|
||||
if (jsonObject.metadata.oldestForwardsCompatibleVersion) {
|
||||
// Sanity check
|
||||
if (jsonObject.metadata.oldestForwardsCompatibleVersion > schemaVersion) {
|
||||
throw new Error(
|
||||
`Error loading ${apiJsonFilename}:` +
|
||||
`\nInvalid file format; "oldestForwardsCompatibleVersion" cannot be newer than "schemaVersion"`,
|
||||
);
|
||||
}
|
||||
|
||||
oldestForwardsCompatibleVersion = jsonObject.metadata.oldestForwardsCompatibleVersion;
|
||||
}
|
||||
|
||||
let versionToDeserialize: number = schemaVersion;
|
||||
if (versionToDeserialize > ApiJsonSchemaVersion.LATEST) {
|
||||
// If the file format is too new, can we treat it as some earlier compatible version
|
||||
// as indicated by oldestForwardsCompatibleVersion?
|
||||
versionToDeserialize = Math.max(oldestForwardsCompatibleVersion, ApiJsonSchemaVersion.LATEST);
|
||||
|
||||
if (versionToDeserialize > ApiJsonSchemaVersion.LATEST) {
|
||||
// Nope, still too new
|
||||
throw new Error(
|
||||
`Error loading ${apiJsonFilename}:` +
|
||||
`\nThe file format version ${schemaVersion} was written by a newer release of` +
|
||||
` the api-extractor-model library; you may need to upgrade your software`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration();
|
||||
|
||||
if (versionToDeserialize >= ApiJsonSchemaVersion.V_1004) {
|
||||
const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromObject(jsonObject.metadata.tsdocConfig);
|
||||
if (tsdocConfigFile.hasErrors) {
|
||||
throw new Error(`Error loading ${apiJsonFilename}:\n` + tsdocConfigFile.getErrorSummary());
|
||||
}
|
||||
|
||||
tsdocConfigFile.configureParser(tsdocConfiguration);
|
||||
}
|
||||
|
||||
const context: DeserializerContext = new DeserializerContext({
|
||||
apiJsonFilename,
|
||||
toolPackage: jsonObject.metadata.toolPackage,
|
||||
toolVersion: jsonObject.metadata.toolVersion,
|
||||
versionToDeserialize,
|
||||
tsdocConfiguration,
|
||||
});
|
||||
|
||||
return ApiItem.deserialize(jsonObject, context) as ApiPackage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Package;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
// No prefix needed, because ApiPackage is the only possible member of an ApiModel
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public get entryPoints(): readonly ApiEntryPoint[] {
|
||||
return this.members as readonly ApiEntryPoint[];
|
||||
}
|
||||
|
||||
/**
|
||||
* The TSDoc configuration that was used when analyzing the API for this package.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Normally this configuration is loaded from the project's tsdoc.json file. It is stored
|
||||
* in the .api.json file so that doc comments can be parsed accurately when loading the file.
|
||||
*/
|
||||
public get tsdocConfiguration(): TSDocConfiguration {
|
||||
return this._tsdocConfiguration;
|
||||
}
|
||||
|
||||
public get projectFolderUrl(): string | undefined {
|
||||
return this._projectFolderUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override addMember(member: ApiEntryPoint): void {
|
||||
if (member.kind !== ApiItemKind.EntryPoint) {
|
||||
throw new Error('Only items of type ApiEntryPoint may be added to an ApiPackage');
|
||||
}
|
||||
|
||||
super.addMember(member);
|
||||
}
|
||||
|
||||
public findEntryPointsByPath(importPath: string): readonly ApiEntryPoint[] {
|
||||
return this.findMembersByName(importPath) as readonly ApiEntryPoint[];
|
||||
}
|
||||
|
||||
public saveToJsonFile(apiJsonFilename: string, options?: IApiPackageSaveOptions): void {
|
||||
const ioptions = options ?? {};
|
||||
|
||||
const packageJson: IPackageJson = PackageJsonLookup.loadOwnPackageJson(__dirname);
|
||||
|
||||
const tsdocConfigFile: TSDocConfigFile = TSDocConfigFile.loadFromParser(this.tsdocConfiguration);
|
||||
const tsdocConfig: JsonObject = tsdocConfigFile.saveToObject();
|
||||
|
||||
const jsonObject: IApiPackageJson = {
|
||||
metadata: {
|
||||
toolPackage: ioptions.toolPackage ?? packageJson.name,
|
||||
// In test mode, we don't write the real version, since that would cause spurious diffs whenever
|
||||
// the version is bumped. Instead we write a placeholder string.
|
||||
toolVersion: ioptions.testMode ? '[test mode]' : ioptions.toolVersion ?? packageJson.version,
|
||||
schemaVersion: ApiJsonSchemaVersion.LATEST,
|
||||
oldestForwardsCompatibleVersion: ApiJsonSchemaVersion.OLDEST_FORWARDS_COMPATIBLE,
|
||||
tsdocConfig,
|
||||
},
|
||||
} as IApiPackageJson;
|
||||
|
||||
if (this.projectFolderUrl) {
|
||||
jsonObject.projectFolderUrl = this.projectFolderUrl;
|
||||
}
|
||||
|
||||
this.serializeInto(jsonObject);
|
||||
JsonFile.save(jsonObject, apiJsonFilename, ioptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
return DeclarationReference.package(this.name);
|
||||
}
|
||||
}
|
||||
95
packages/api-extractor-model/src/model/ApiProperty.ts
Normal file
95
packages/api-extractor-model/src/model/ApiProperty.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { ApiPropertyItem, type IApiPropertyItemOptions } from '../items/ApiPropertyItem.js';
|
||||
import { ApiAbstractMixin, type IApiAbstractMixinOptions } from '../mixins/ApiAbstractMixin.js';
|
||||
import { ApiInitializerMixin, type IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin.js';
|
||||
import { ApiProtectedMixin, type IApiProtectedMixinOptions } from '../mixins/ApiProtectedMixin.js';
|
||||
import { ApiStaticMixin, type IApiStaticMixinOptions } from '../mixins/ApiStaticMixin.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiProperty}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiPropertyOptions
|
||||
extends IApiPropertyItemOptions,
|
||||
IApiAbstractMixinOptions,
|
||||
IApiProtectedMixinOptions,
|
||||
IApiStaticMixinOptions,
|
||||
IApiInitializerMixinOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript property declaration that belongs to an `ApiClass`.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiProperty` represents a TypeScript declaration such as the `width` and `height` members in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export class Widget {
|
||||
* public width: number = 100;
|
||||
*
|
||||
* public get height(): number {
|
||||
* if (this.isSquashed()) {
|
||||
* return 0;
|
||||
* } else {
|
||||
* return this.clientArea.height;
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Note that member variables are also considered to be properties.
|
||||
*
|
||||
* If the property has both a getter function and setter function, they will be represented by a single `ApiProperty`
|
||||
* and must have a single documentation comment.
|
||||
*
|
||||
* Compare with {@link ApiPropertySignature}, which represents a property belonging to an interface.
|
||||
* For example, a class property can be `static` but an interface property cannot.
|
||||
* @public
|
||||
*/
|
||||
export class ApiProperty extends ApiAbstractMixin(
|
||||
ApiProtectedMixin(ApiStaticMixin(ApiInitializerMixin(ApiPropertyItem))),
|
||||
) {
|
||||
public constructor(options: IApiPropertyOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string, isStatic: boolean): string {
|
||||
if (isStatic) {
|
||||
return `${name}|${ApiItemKind.Property}|static`;
|
||||
} else {
|
||||
return `${name}|${ApiItemKind.Property}|instance`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Property;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiProperty.getContainerKey(this.name, this.isStatic);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep((this.isStatic ? Navigation.Exports : Navigation.Members) as any, nameComponent)
|
||||
.withMeaning(Meaning.Member as any);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import { ApiPropertyItem, type IApiPropertyItemOptions } from '../items/ApiPropertyItem.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiPropertySignature}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiPropertySignatureOptions extends IApiPropertyItemOptions {}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript property declaration that belongs to an `ApiInterface`.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiPropertySignature` represents a TypeScript declaration such as the `width` and `height` members in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export interface IWidget {
|
||||
* readonly width: number;
|
||||
* height: number;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Compare with {@link ApiProperty}, which represents a property belonging to a class.
|
||||
* For example, a class property can be `static` but an interface property cannot.
|
||||
* @public
|
||||
*/
|
||||
export class ApiPropertySignature extends ApiPropertyItem {
|
||||
public constructor(options: IApiPropertySignatureOptions) {
|
||||
super(options);
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.PropertySignature}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.PropertySignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiPropertySignature.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(Navigation.Members as any, nameComponent)
|
||||
.withMeaning(Meaning.Member as any);
|
||||
}
|
||||
}
|
||||
137
packages/api-extractor-model/src/model/ApiTypeAlias.ts
Normal file
137
packages/api-extractor-model/src/model/ApiTypeAlias.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions, type IApiDeclaredItemJson } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import {
|
||||
type IApiExportedMixinJson,
|
||||
type IApiExportedMixinOptions,
|
||||
ApiExportedMixin,
|
||||
} from '../mixins/ApiExportedMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import {
|
||||
ApiTypeParameterListMixin,
|
||||
type IApiTypeParameterListMixinOptions,
|
||||
type IApiTypeParameterListMixinJson,
|
||||
} from '../mixins/ApiTypeParameterListMixin.js';
|
||||
import type { Excerpt, IExcerptTokenRange } from '../mixins/Excerpt.js';
|
||||
import type { DeserializerContext } from './DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiTypeAlias}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiTypeAliasOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiTypeParameterListMixinOptions,
|
||||
IApiExportedMixinOptions {
|
||||
typeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
export interface IApiTypeAliasJson extends IApiDeclaredItemJson, IApiTypeParameterListMixinJson, IApiExportedMixinJson {
|
||||
typeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript type alias declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiTypeAlias` represents a definition such as one of these examples:
|
||||
*
|
||||
* ```ts
|
||||
* // A union type:
|
||||
* export type Shape = Square | Triangle | Circle;
|
||||
*
|
||||
* // A generic type alias:
|
||||
* export type BoxedValue<T> = { value: T };
|
||||
*
|
||||
* export type BoxedArray<T> = { array: T[] };
|
||||
*
|
||||
* // A conditional type alias:
|
||||
* export type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
|
||||
*
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiTypeAlias extends ApiTypeParameterListMixin(
|
||||
ApiNameMixin(ApiReleaseTagMixin(ApiExportedMixin(ApiDeclaredItem))),
|
||||
) {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the type of the alias.
|
||||
*
|
||||
* @remarks
|
||||
* In the example below, the `typeExcerpt` would correspond to the subexpression
|
||||
* `T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;`:
|
||||
*
|
||||
* ```ts
|
||||
* export type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
|
||||
* ```
|
||||
*/
|
||||
public readonly typeExcerpt: Excerpt;
|
||||
|
||||
public constructor(options: IApiTypeAliasOptions) {
|
||||
super(options);
|
||||
|
||||
this.typeExcerpt = this.buildExcerpt(options.typeTokenRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiTypeAliasOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiTypeAliasJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.typeTokenRange = jsonObject.typeTokenRange;
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.TypeAlias}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.TypeAlias;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiTypeAlias.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiTypeAliasJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.typeTokenRange = this.typeExcerpt.tokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.TypeAlias as any);
|
||||
}
|
||||
}
|
||||
121
packages/api-extractor-model/src/model/ApiVariable.ts
Normal file
121
packages/api-extractor-model/src/model/ApiVariable.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { DeclarationReference, type Component } from '@microsoft/tsdoc/lib-commonjs/beta/DeclarationReference.js';
|
||||
import { ApiDeclaredItem, type IApiDeclaredItemOptions, type IApiDeclaredItemJson } from '../items/ApiDeclaredItem.js';
|
||||
import { ApiItemKind, Navigation, Meaning } from '../items/ApiItem.js';
|
||||
import {
|
||||
type IApiExportedMixinJson,
|
||||
type IApiExportedMixinOptions,
|
||||
ApiExportedMixin,
|
||||
} from '../mixins/ApiExportedMixin.js';
|
||||
import { ApiInitializerMixin, type IApiInitializerMixinOptions } from '../mixins/ApiInitializerMixin.js';
|
||||
import { type IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin.js';
|
||||
import { ApiReadonlyMixin, type IApiReadonlyMixinOptions } from '../mixins/ApiReadonlyMixin.js';
|
||||
import { ApiReleaseTagMixin, type IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin.js';
|
||||
import type { IExcerptTokenRange, Excerpt } from '../mixins/Excerpt.js';
|
||||
import type { DeserializerContext } from './DeserializerContext.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link ApiVariable}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IApiVariableOptions
|
||||
extends IApiNameMixinOptions,
|
||||
IApiReleaseTagMixinOptions,
|
||||
IApiReadonlyMixinOptions,
|
||||
IApiDeclaredItemOptions,
|
||||
IApiInitializerMixinOptions,
|
||||
IApiExportedMixinOptions {
|
||||
variableTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
export interface IApiVariableJson extends IApiDeclaredItemJson, IApiExportedMixinJson {
|
||||
variableTypeTokenRange: IExcerptTokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a TypeScript variable declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is part of the {@link ApiModel} hierarchy of classes, which are serializable representations of
|
||||
* API declarations.
|
||||
*
|
||||
* `ApiVariable` represents an exported `const` or `let` object such as these examples:
|
||||
*
|
||||
* ```ts
|
||||
* // A variable declaration
|
||||
* export let verboseLogging: boolean;
|
||||
*
|
||||
* // A constant variable declaration with an initializer
|
||||
* export const canvas: IWidget = createCanvas();
|
||||
* ```
|
||||
* @public
|
||||
*/
|
||||
export class ApiVariable extends ApiNameMixin(
|
||||
ApiReleaseTagMixin(ApiReadonlyMixin(ApiInitializerMixin(ApiExportedMixin(ApiDeclaredItem)))),
|
||||
) {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the type of the variable.
|
||||
*/
|
||||
public readonly variableTypeExcerpt: Excerpt;
|
||||
|
||||
public constructor(options: IApiVariableOptions) {
|
||||
super(options);
|
||||
|
||||
this.variableTypeExcerpt = this.buildExcerpt(options.variableTypeTokenRange);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public static override onDeserializeInto(
|
||||
options: Partial<IApiVariableOptions>,
|
||||
context: DeserializerContext,
|
||||
jsonObject: IApiVariableJson,
|
||||
): void {
|
||||
super.onDeserializeInto(options, context, jsonObject);
|
||||
|
||||
options.variableTypeTokenRange = jsonObject.variableTypeTokenRange;
|
||||
}
|
||||
|
||||
public static getContainerKey(name: string): string {
|
||||
return `${name}|${ApiItemKind.Variable}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get kind(): ApiItemKind {
|
||||
return ApiItemKind.Variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override get containerKey(): string {
|
||||
return ApiVariable.getContainerKey(this.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public override serializeInto(jsonObject: Partial<IApiVariableJson>): void {
|
||||
super.serializeInto(jsonObject);
|
||||
|
||||
jsonObject.variableTypeTokenRange = this.variableTypeExcerpt.tokenRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @beta @override
|
||||
*/
|
||||
public override buildCanonicalReference(): DeclarationReference {
|
||||
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
|
||||
const navigation: Navigation = this.isExported ? Navigation.Exports : Navigation.Locals;
|
||||
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
|
||||
.addNavigationStep(navigation as any, nameComponent)
|
||||
.withMeaning(Meaning.Variable as any);
|
||||
}
|
||||
}
|
||||
93
packages/api-extractor-model/src/model/Deserializer.ts
Normal file
93
packages/api-extractor-model/src/model/Deserializer.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { IApiDeclaredItemJson } from '../items/ApiDeclaredItem.js';
|
||||
import { type IApiItemJson, type IApiItemOptions, type ApiItem, ApiItemKind } from '../items/ApiItem.js';
|
||||
import type { IApiPropertyItemJson } from '../items/ApiPropertyItem.js';
|
||||
import { ApiCallSignature, type IApiCallSignatureOptions } from './ApiCallSignature.js';
|
||||
import { ApiClass, type IApiClassOptions, type IApiClassJson } from './ApiClass.js';
|
||||
import { ApiConstructSignature, type IApiConstructSignatureOptions } from './ApiConstructSignature.js';
|
||||
import { ApiConstructor, type IApiConstructorOptions } from './ApiConstructor.js';
|
||||
import { ApiEntryPoint, type IApiEntryPointOptions } from './ApiEntryPoint.js';
|
||||
import { ApiEnum, type IApiEnumOptions } from './ApiEnum.js';
|
||||
import { ApiEnumMember, type IApiEnumMemberOptions } from './ApiEnumMember.js';
|
||||
import { ApiFunction, type IApiFunctionOptions } from './ApiFunction.js';
|
||||
import { ApiIndexSignature, type IApiIndexSignatureOptions } from './ApiIndexSignature.js';
|
||||
import { ApiInterface, type IApiInterfaceOptions, type IApiInterfaceJson } from './ApiInterface.js';
|
||||
import { ApiMethod, type IApiMethodOptions } from './ApiMethod.js';
|
||||
import { ApiMethodSignature, type IApiMethodSignatureOptions } from './ApiMethodSignature.js';
|
||||
import { ApiModel } from './ApiModel.js';
|
||||
import { ApiNamespace, type IApiNamespaceOptions } from './ApiNamespace.js';
|
||||
import { ApiPackage, type IApiPackageOptions, type IApiPackageJson } from './ApiPackage.js';
|
||||
import { ApiProperty, type IApiPropertyOptions } from './ApiProperty.js';
|
||||
import { ApiPropertySignature, type IApiPropertySignatureOptions } from './ApiPropertySignature.js';
|
||||
import { ApiTypeAlias, type IApiTypeAliasOptions, type IApiTypeAliasJson } from './ApiTypeAlias.js';
|
||||
import { ApiVariable, type IApiVariableOptions, type IApiVariableJson } from './ApiVariable.js';
|
||||
import type { DeserializerContext } from './DeserializerContext.js';
|
||||
|
||||
export class Deserializer {
|
||||
public static deserialize(context: DeserializerContext, jsonObject: IApiItemJson): ApiItem {
|
||||
const options: Partial<IApiItemOptions> = {};
|
||||
|
||||
switch (jsonObject.kind) {
|
||||
case ApiItemKind.Class:
|
||||
ApiClass.onDeserializeInto(options, context, jsonObject as IApiClassJson);
|
||||
return new ApiClass(options as IApiClassOptions);
|
||||
case ApiItemKind.CallSignature:
|
||||
ApiCallSignature.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiCallSignature(options as IApiCallSignatureOptions);
|
||||
case ApiItemKind.Constructor:
|
||||
ApiConstructor.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiConstructor(options as IApiConstructorOptions);
|
||||
case ApiItemKind.ConstructSignature:
|
||||
ApiConstructSignature.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiConstructSignature(options as IApiConstructSignatureOptions);
|
||||
case ApiItemKind.EntryPoint:
|
||||
ApiEntryPoint.onDeserializeInto(options, context, jsonObject);
|
||||
return new ApiEntryPoint(options as IApiEntryPointOptions);
|
||||
case ApiItemKind.Enum:
|
||||
ApiEnum.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiEnum(options as IApiEnumOptions);
|
||||
case ApiItemKind.EnumMember:
|
||||
ApiEnumMember.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiEnumMember(options as IApiEnumMemberOptions);
|
||||
case ApiItemKind.Function:
|
||||
ApiFunction.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiFunction(options as IApiFunctionOptions);
|
||||
case ApiItemKind.IndexSignature:
|
||||
ApiIndexSignature.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiIndexSignature(options as IApiIndexSignatureOptions);
|
||||
case ApiItemKind.Interface:
|
||||
ApiInterface.onDeserializeInto(options, context, jsonObject as IApiInterfaceJson);
|
||||
return new ApiInterface(options as IApiInterfaceOptions);
|
||||
case ApiItemKind.Method:
|
||||
ApiMethod.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiMethod(options as IApiMethodOptions);
|
||||
case ApiItemKind.MethodSignature:
|
||||
ApiMethodSignature.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiMethodSignature(options as IApiMethodSignatureOptions);
|
||||
case ApiItemKind.Model:
|
||||
return new ApiModel();
|
||||
case ApiItemKind.Namespace:
|
||||
ApiNamespace.onDeserializeInto(options, context, jsonObject as IApiDeclaredItemJson);
|
||||
return new ApiNamespace(options as IApiNamespaceOptions);
|
||||
case ApiItemKind.Package:
|
||||
ApiPackage.onDeserializeInto(options, context, jsonObject as IApiPackageJson);
|
||||
return new ApiPackage(options as IApiPackageOptions);
|
||||
case ApiItemKind.Property:
|
||||
ApiProperty.onDeserializeInto(options, context, jsonObject as IApiPropertyItemJson);
|
||||
return new ApiProperty(options as IApiPropertyOptions);
|
||||
case ApiItemKind.PropertySignature:
|
||||
ApiPropertySignature.onDeserializeInto(options, context, jsonObject as IApiPropertyItemJson);
|
||||
return new ApiPropertySignature(options as IApiPropertySignatureOptions);
|
||||
case ApiItemKind.TypeAlias:
|
||||
ApiTypeAlias.onDeserializeInto(options, context, jsonObject as IApiTypeAliasJson);
|
||||
return new ApiTypeAlias(options as IApiTypeAliasOptions);
|
||||
case ApiItemKind.Variable:
|
||||
ApiVariable.onDeserializeInto(options, context, jsonObject as IApiVariableJson);
|
||||
return new ApiVariable(options as IApiVariableOptions);
|
||||
default:
|
||||
throw new Error(`Failed to deserialize unsupported API item type ${JSON.stringify(jsonObject.kind)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
packages/api-extractor-model/src/model/DeserializerContext.ts
Normal file
153
packages/api-extractor-model/src/model/DeserializerContext.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/* eslint-disable @typescript-eslint/prefer-literal-enum-member */
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { TSDocConfiguration } from '@microsoft/tsdoc';
|
||||
|
||||
export enum ApiJsonSchemaVersion {
|
||||
/**
|
||||
* The initial release.
|
||||
*/
|
||||
V_1000 = 1_000,
|
||||
|
||||
/**
|
||||
* Add support for type parameters and type alias types.
|
||||
*/
|
||||
V_1001 = 1_001,
|
||||
|
||||
/**
|
||||
* Remove `canonicalReference` field. This field was for diagnostic purposes only and was never deserialized.
|
||||
*/
|
||||
V_1002 = 1_002,
|
||||
|
||||
/**
|
||||
* Reintroduce the `canonicalReference` field using the experimental new TSDoc declaration reference notation.
|
||||
*
|
||||
* This is not a breaking change because this field is never deserialized; it is provided for informational
|
||||
* purposes only.
|
||||
*/
|
||||
V_1003 = 1_003,
|
||||
|
||||
/**
|
||||
* Add a `tsdocConfig` field that tracks the TSDoc configuration for parsing doc comments.
|
||||
*
|
||||
* This is not a breaking change because an older implementation will still work correctly. The
|
||||
* custom tags will be skipped over by the parser.
|
||||
*/
|
||||
V_1004 = 1_004,
|
||||
|
||||
/**
|
||||
* Add an `isOptional` field to `Parameter` and `TypeParameter` to track whether a function parameter is optional.
|
||||
*
|
||||
* When loading older JSON files, the value defaults to `false`.
|
||||
*/
|
||||
V_1005 = 1_005,
|
||||
|
||||
/**
|
||||
* Add an `isProtected` field to `ApiConstructor`, `ApiMethod`, and `ApiProperty` to
|
||||
* track whether a class member has the `protected` modifier.
|
||||
*
|
||||
* Add an `isReadonly` field to `ApiProperty`, `ApiPropertySignature`, and `ApiVariable` to
|
||||
* track whether the item is readonly.
|
||||
*
|
||||
* When loading older JSON files, the values default to `false`.
|
||||
*/
|
||||
V_1006 = 1_006,
|
||||
|
||||
/**
|
||||
* Add `ApiItemContainerMixin.preserveMemberOrder` to support enums that preserve their original sort order.
|
||||
*
|
||||
* When loading older JSON files, the value default to `false`.
|
||||
*/
|
||||
V_1007 = 1_007,
|
||||
|
||||
/**
|
||||
* Add an `initializerTokenRange` field to `ApiProperty` and `ApiVariable` to track the item's
|
||||
* initializer.
|
||||
*
|
||||
* When loading older JSON files, this range is empty.
|
||||
*/
|
||||
V_1008 = 1_008,
|
||||
|
||||
/**
|
||||
* Add an `isReadonly` field to `ApiIndexSignature` to track whether the item is readonly.
|
||||
*
|
||||
* When loading older JSON files, the values defaults to `false`.
|
||||
*/
|
||||
V_1009 = 1_009,
|
||||
|
||||
/**
|
||||
* Add a `fileUrlPath` field to `ApiDeclaredItem` to track the URL to a declared item's source file.
|
||||
*
|
||||
* When loading older JSON files, the value defaults to `undefined`.
|
||||
*/
|
||||
V_1010 = 1_010,
|
||||
|
||||
/**
|
||||
* Add an `isAbstract` field to `ApiClass`, `ApiMethod`, and `ApiProperty` to
|
||||
* track whether the item is abstract.
|
||||
*
|
||||
* When loading older JSON files, the value defaults to `false`.
|
||||
*/
|
||||
V_1011 = 1_011,
|
||||
|
||||
/**
|
||||
* The current latest .api.json schema version.
|
||||
*
|
||||
* IMPORTANT: When incrementing this number, consider whether `OLDEST_SUPPORTED` or `OLDEST_FORWARDS_COMPATIBLE`
|
||||
* should be updated.
|
||||
*/
|
||||
LATEST = V_1011,
|
||||
|
||||
/**
|
||||
* The oldest .api.json schema version that is still supported for backwards compatibility.
|
||||
*
|
||||
* This must be updated if you change to the file format and do not implement compatibility logic for
|
||||
* deserializing the older representation.
|
||||
*/
|
||||
OLDEST_SUPPORTED = V_1001,
|
||||
|
||||
/**
|
||||
* Used to assign `IApiPackageMetadataJson.oldestForwardsCompatibleVersion`.
|
||||
*
|
||||
* This value must be \<= `ApiJsonSchemaVersion.LATEST`. It must be reset to the `LATEST` value
|
||||
* if the older library would not be able to deserialize your new file format. Adding a nonessential field
|
||||
* is generally okay. Removing, modifying, or reinterpreting existing fields is NOT safe.
|
||||
*/
|
||||
OLDEST_FORWARDS_COMPATIBLE = V_1001,
|
||||
}
|
||||
|
||||
export class DeserializerContext {
|
||||
/**
|
||||
* The path of the file being deserialized, which may be useful for diagnostic purposes.
|
||||
*/
|
||||
public readonly apiJsonFilename: string;
|
||||
|
||||
/**
|
||||
* Metadata from `IApiPackageMetadataJson.toolPackage`.
|
||||
*/
|
||||
public readonly toolPackage: string;
|
||||
|
||||
/**
|
||||
* Metadata from `IApiPackageMetadataJson.toolVersion`.
|
||||
*/
|
||||
public readonly toolVersion: string;
|
||||
|
||||
/**
|
||||
* The version of the schema being deserialized, as obtained from `IApiPackageMetadataJson.schemaVersion`.
|
||||
*/
|
||||
public readonly versionToDeserialize: ApiJsonSchemaVersion;
|
||||
|
||||
/**
|
||||
* The TSDoc configuration for the context.
|
||||
*/
|
||||
public readonly tsdocConfiguration: TSDocConfiguration;
|
||||
|
||||
public constructor(options: DeserializerContext) {
|
||||
this.apiJsonFilename = options.apiJsonFilename;
|
||||
this.toolPackage = options.toolPackage;
|
||||
this.toolVersion = options.toolVersion;
|
||||
this.versionToDeserialize = options.versionToDeserialize;
|
||||
this.tsdocConfiguration = options.tsdocConfiguration;
|
||||
}
|
||||
}
|
||||
44
packages/api-extractor-model/src/model/HeritageType.ts
Normal file
44
packages/api-extractor-model/src/model/HeritageType.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type { Excerpt } from '../mixins/Excerpt.js';
|
||||
|
||||
/**
|
||||
* Represents a type referenced via an "extends" or "implements" heritage clause for a TypeScript class
|
||||
* or interface.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* For example, consider this declaration:
|
||||
*
|
||||
* ```ts
|
||||
* export class Widget extends Controls.WidgetBase implements Controls.IWidget, IDisposable {
|
||||
* // . . .
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The heritage types are `Controls.WidgetBase`, `Controls.IWidget`, and `IDisposable`.
|
||||
* @public
|
||||
*/
|
||||
export class HeritageType {
|
||||
/**
|
||||
* An excerpt corresponding to the referenced type.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* For example, consider this declaration:
|
||||
*
|
||||
* ```ts
|
||||
* export class Widget extends Controls.WidgetBase implements Controls.IWidget, IDisposable {
|
||||
* // . . .
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The excerpt might be `Controls.WidgetBase`, `Controls.IWidget`, or `IDisposable`.
|
||||
*/
|
||||
public readonly excerpt: Excerpt;
|
||||
|
||||
public constructor(excerpt: Excerpt) {
|
||||
this.excerpt = excerpt;
|
||||
}
|
||||
}
|
||||
235
packages/api-extractor-model/src/model/ModelReferenceResolver.ts
Normal file
235
packages/api-extractor-model/src/model/ModelReferenceResolver.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { type DocDeclarationReference, type DocMemberSelector, SelectorKind } from '@microsoft/tsdoc';
|
||||
import { type ApiItem, ApiItemKind } from '../items/ApiItem.js';
|
||||
import { ApiItemContainerMixin } from '../mixins/ApiItemContainerMixin.js';
|
||||
import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import type { ApiEntryPoint } from './ApiEntryPoint.js';
|
||||
import type { ApiModel } from './ApiModel.js';
|
||||
import type { ApiPackage } from './ApiPackage.js';
|
||||
|
||||
/**
|
||||
* Result object for {@link ApiModel.resolveDeclarationReference}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IResolveDeclarationReferenceResult {
|
||||
/**
|
||||
* If resolvedApiItem is undefined, then this will always contain an error message explaining why the
|
||||
* resolution failed.
|
||||
*/
|
||||
errorMessage: string | undefined;
|
||||
|
||||
/**
|
||||
* The referenced ApiItem, if the declaration reference could be resolved.
|
||||
*/
|
||||
resolvedApiItem: ApiItem | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* This resolves a TSDoc declaration reference by walking the `ApiModel` hierarchy.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This class is analogous to `AstReferenceResolver` from the `@microsoft/api-extractor` project,
|
||||
* which resolves declaration references by walking the compiler state.
|
||||
*/
|
||||
export class ModelReferenceResolver {
|
||||
private readonly _apiModel: ApiModel;
|
||||
|
||||
public constructor(apiModel: ApiModel) {
|
||||
this._apiModel = apiModel;
|
||||
}
|
||||
|
||||
public resolve(
|
||||
declarationReference: DocDeclarationReference,
|
||||
contextApiItem: ApiItem | undefined,
|
||||
): IResolveDeclarationReferenceResult {
|
||||
const result: IResolveDeclarationReferenceResult = {
|
||||
resolvedApiItem: undefined,
|
||||
errorMessage: undefined,
|
||||
};
|
||||
|
||||
let apiPackage: ApiPackage | undefined;
|
||||
|
||||
// Is this an absolute reference?
|
||||
if (declarationReference.packageName === undefined) {
|
||||
// If the package name is omitted, try to infer it from the context
|
||||
if (contextApiItem !== undefined) {
|
||||
apiPackage = contextApiItem.getAssociatedPackage();
|
||||
}
|
||||
|
||||
if (apiPackage === undefined) {
|
||||
result.errorMessage = `The reference does not include a package name, and the package could not be inferred from the context`;
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
apiPackage = this._apiModel.tryGetPackageByName(declarationReference.packageName);
|
||||
if (apiPackage === undefined) {
|
||||
result.errorMessage = `The package "${declarationReference.packageName}" could not be located`;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const importPath: string = declarationReference.importPath ?? '';
|
||||
|
||||
const foundEntryPoints: readonly ApiEntryPoint[] = apiPackage.findEntryPointsByPath(importPath);
|
||||
if (foundEntryPoints.length < 1) {
|
||||
result.errorMessage = `The import path "${importPath}" could not be resolved`;
|
||||
return result;
|
||||
}
|
||||
|
||||
let currentItem: ApiItem = foundEntryPoints[0]!;
|
||||
|
||||
// Now search for the member reference
|
||||
for (const memberReference of declarationReference.memberReferences) {
|
||||
if (memberReference.memberSymbol !== undefined) {
|
||||
result.errorMessage = `Symbols are not yet supported in declaration references`;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (memberReference.memberIdentifier === undefined) {
|
||||
result.errorMessage = `Missing member identifier`;
|
||||
return result;
|
||||
}
|
||||
|
||||
const identifier: string = memberReference.memberIdentifier.identifier;
|
||||
|
||||
if (!ApiItemContainerMixin.isBaseClassOf(currentItem)) {
|
||||
// For example, {@link MyClass.myMethod.X} is invalid because methods cannot contain members
|
||||
result.errorMessage = `Unable to resolve ${JSON.stringify(
|
||||
identifier,
|
||||
)} because ${currentItem.getScopedNameWithinPackage()} cannot act as a container`;
|
||||
return result;
|
||||
}
|
||||
|
||||
const foundMembers: readonly ApiItem[] = currentItem.findMembersByName(identifier);
|
||||
if (foundMembers.length === 0) {
|
||||
result.errorMessage = `The member reference ${JSON.stringify(identifier)} was not found`;
|
||||
return result;
|
||||
}
|
||||
|
||||
const memberSelector: DocMemberSelector | undefined = memberReference.selector;
|
||||
if (memberSelector === undefined) {
|
||||
if (foundMembers.length > 1) {
|
||||
result.errorMessage = `The member reference ${JSON.stringify(identifier)} was ambiguous`;
|
||||
return result;
|
||||
}
|
||||
|
||||
currentItem = foundMembers[0]!;
|
||||
} else {
|
||||
let memberSelectorResult: IResolveDeclarationReferenceResult;
|
||||
switch (memberSelector.selectorKind) {
|
||||
case SelectorKind.System:
|
||||
memberSelectorResult = this._selectUsingSystemSelector(foundMembers, memberSelector, identifier);
|
||||
break;
|
||||
case SelectorKind.Index:
|
||||
memberSelectorResult = this._selectUsingIndexSelector(foundMembers, memberSelector, identifier);
|
||||
break;
|
||||
default:
|
||||
result.errorMessage = `The selector "${memberSelector.selector}" is not a supported selector type`;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (memberSelectorResult.resolvedApiItem === undefined) {
|
||||
return memberSelectorResult;
|
||||
}
|
||||
|
||||
currentItem = memberSelectorResult.resolvedApiItem;
|
||||
}
|
||||
}
|
||||
|
||||
result.resolvedApiItem = currentItem;
|
||||
return result;
|
||||
}
|
||||
|
||||
private _selectUsingSystemSelector(
|
||||
foundMembers: readonly ApiItem[],
|
||||
memberSelector: DocMemberSelector,
|
||||
identifier: string,
|
||||
): IResolveDeclarationReferenceResult {
|
||||
const result: IResolveDeclarationReferenceResult = {
|
||||
resolvedApiItem: undefined,
|
||||
errorMessage: undefined,
|
||||
};
|
||||
|
||||
const selectorName: string = memberSelector.selector;
|
||||
|
||||
let selectorItemKind: ApiItemKind;
|
||||
switch (selectorName) {
|
||||
case 'class':
|
||||
selectorItemKind = ApiItemKind.Class;
|
||||
break;
|
||||
case 'enum':
|
||||
selectorItemKind = ApiItemKind.Enum;
|
||||
break;
|
||||
case 'function':
|
||||
selectorItemKind = ApiItemKind.Function;
|
||||
break;
|
||||
case 'interface':
|
||||
selectorItemKind = ApiItemKind.Interface;
|
||||
break;
|
||||
case 'namespace':
|
||||
selectorItemKind = ApiItemKind.Namespace;
|
||||
break;
|
||||
case 'type':
|
||||
selectorItemKind = ApiItemKind.TypeAlias;
|
||||
break;
|
||||
case 'variable':
|
||||
selectorItemKind = ApiItemKind.Variable;
|
||||
break;
|
||||
default:
|
||||
result.errorMessage = `Unsupported system selector "${selectorName}"`;
|
||||
return result;
|
||||
}
|
||||
|
||||
const matches: ApiItem[] = foundMembers.filter((x) => x.kind === selectorItemKind);
|
||||
if (matches.length === 0) {
|
||||
result.errorMessage = `A declaration for "${identifier}" was not found that matches the TSDoc selector "${selectorName}"`;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (matches.length > 1) {
|
||||
result.errorMessage = `More than one declaration "${identifier}" matches the TSDoc selector "${selectorName}"`;
|
||||
}
|
||||
|
||||
result.resolvedApiItem = matches[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
private _selectUsingIndexSelector(
|
||||
foundMembers: readonly ApiItem[],
|
||||
memberSelector: DocMemberSelector,
|
||||
identifier: string,
|
||||
): IResolveDeclarationReferenceResult {
|
||||
const result: IResolveDeclarationReferenceResult = {
|
||||
resolvedApiItem: undefined,
|
||||
errorMessage: undefined,
|
||||
};
|
||||
|
||||
const selectedMembers: ApiItem[] = [];
|
||||
|
||||
const selectorOverloadIndex: number = Number.parseInt(memberSelector.selector, 10);
|
||||
for (const foundMember of foundMembers) {
|
||||
if (ApiParameterListMixin.isBaseClassOf(foundMember) && foundMember.overloadIndex === selectorOverloadIndex) {
|
||||
selectedMembers.push(foundMember);
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedMembers.length === 0) {
|
||||
result.errorMessage =
|
||||
`An overload for ${JSON.stringify(identifier)} was not found that matches` +
|
||||
` the TSDoc selector ":${selectorOverloadIndex}"`;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (selectedMembers.length === 1) {
|
||||
result.resolvedApiItem = selectedMembers[0];
|
||||
return result;
|
||||
}
|
||||
|
||||
result.errorMessage = `The member reference ${JSON.stringify(identifier)} was ambiguous`;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
79
packages/api-extractor-model/src/model/Parameter.ts
Normal file
79
packages/api-extractor-model/src/model/Parameter.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type * as tsdoc from '@microsoft/tsdoc';
|
||||
import { ApiDocumentedItem } from '../items/ApiDocumentedItem.js';
|
||||
import type { ApiParameterListMixin } from '../mixins/ApiParameterListMixin.js';
|
||||
import type { Excerpt } from '../mixins/Excerpt.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link Parameter}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface IParameterOptions {
|
||||
isOptional: boolean;
|
||||
isRest: boolean;
|
||||
name: string;
|
||||
parameterTypeExcerpt: Excerpt;
|
||||
parent: ApiParameterListMixin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a named parameter for a function-like declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* `Parameter` represents a TypeScript declaration such as `x: number` in this example:
|
||||
*
|
||||
* ```ts
|
||||
* export function add(x: number, y: number): number {
|
||||
* return x + y;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* `Parameter` objects belong to the {@link (ApiParameterListMixin:interface).parameters} collection.
|
||||
* @public
|
||||
*/
|
||||
export class Parameter {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the type of the parameter.
|
||||
*/
|
||||
public readonly parameterTypeExcerpt: Excerpt;
|
||||
|
||||
/**
|
||||
* The parameter name.
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* Whether the parameter is optional.
|
||||
*/
|
||||
public isOptional: boolean;
|
||||
|
||||
/**
|
||||
* Whether the parameter is a rest parameter
|
||||
*/
|
||||
public isRest: boolean;
|
||||
|
||||
private readonly _parent: ApiParameterListMixin;
|
||||
|
||||
public constructor(options: IParameterOptions) {
|
||||
this.name = options.name;
|
||||
this.parameterTypeExcerpt = options.parameterTypeExcerpt;
|
||||
this.isOptional = options.isOptional;
|
||||
this.isRest = options.isRest;
|
||||
this._parent = options.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `@param` documentation for this parameter, if present.
|
||||
*/
|
||||
public get tsdocParamBlock(): tsdoc.DocParamBlock | undefined {
|
||||
if (this._parent instanceof ApiDocumentedItem && this._parent.tsdocComment) {
|
||||
return this._parent.tsdocComment.params.tryGetBlockByName(this.name);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
91
packages/api-extractor-model/src/model/SourceLocation.ts
Normal file
91
packages/api-extractor-model/src/model/SourceLocation.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import { URL } from 'node:url';
|
||||
|
||||
/**
|
||||
* Constructor options for `SourceLocation`.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ISourceLocationOptions {
|
||||
/**
|
||||
* The file URL path relative to the `projectFolder` and `projectFolderURL` fields as
|
||||
* defined in the `api-extractor.json` config.
|
||||
*/
|
||||
fileUrlPath?: string | undefined;
|
||||
|
||||
/**
|
||||
* The project folder URL as defined by the `api-extractor.json` config `projectFolderUrl`
|
||||
* setting.
|
||||
*/
|
||||
projectFolderUrl?: string | undefined;
|
||||
|
||||
/**
|
||||
* The column number in the source file. The first column number is 1.
|
||||
*/
|
||||
sourceFileColumn?: number | undefined;
|
||||
|
||||
/**
|
||||
* The line number in the source file. The first line number is 1.
|
||||
*/
|
||||
sourceFileLine?: number | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The source location where a given API item is declared.
|
||||
*
|
||||
* @remarks
|
||||
* The source location points to the `.ts` source file where the API item was originally
|
||||
* declared. However, in some cases, if source map resolution fails, it falls back to pointing
|
||||
* to the `.d.ts` file instead.
|
||||
* @public
|
||||
*/
|
||||
export class SourceLocation {
|
||||
private readonly _projectFolderUrl?: string | undefined;
|
||||
|
||||
private readonly _fileUrlPath?: string | undefined;
|
||||
|
||||
private readonly _fileLine?: number | undefined;
|
||||
|
||||
private readonly _fileColumn?: number | undefined;
|
||||
|
||||
public constructor(options: ISourceLocationOptions) {
|
||||
this._projectFolderUrl = options.projectFolderUrl;
|
||||
this._fileUrlPath = options.fileUrlPath;
|
||||
this._fileLine = options.sourceFileLine;
|
||||
this._fileColumn = options.sourceFileColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file URL to the given source location. Returns `undefined` if the file URL
|
||||
* cannot be determined.
|
||||
*/
|
||||
public get fileUrl(): string | undefined {
|
||||
if (this._projectFolderUrl === undefined || this._fileUrlPath === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let projectFolderUrl: string = this._projectFolderUrl;
|
||||
if (!projectFolderUrl.endsWith('/')) {
|
||||
projectFolderUrl += '/';
|
||||
}
|
||||
|
||||
const url: URL = new URL(this._fileUrlPath, projectFolderUrl);
|
||||
return url.href;
|
||||
}
|
||||
|
||||
/**
|
||||
* The line in the `fileUrlPath` where the API item is declared.
|
||||
*/
|
||||
public get fileLine(): number | undefined {
|
||||
return this._fileLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* The column in the `fileUrlPath` where the API item is declared.
|
||||
*/
|
||||
public get fileColumn(): number | undefined {
|
||||
return this._fileColumn;
|
||||
}
|
||||
}
|
||||
106
packages/api-extractor-model/src/model/TypeParameter.ts
Normal file
106
packages/api-extractor-model/src/model/TypeParameter.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||||
// See LICENSE in the project root for license information.
|
||||
|
||||
import type * as tsdoc from '@microsoft/tsdoc';
|
||||
import { ApiDocumentedItem } from '../items/ApiDocumentedItem.js';
|
||||
import type { ApiTypeParameterListMixin } from '../mixins/ApiTypeParameterListMixin.js';
|
||||
import type { Excerpt } from '../mixins/Excerpt.js';
|
||||
|
||||
/**
|
||||
* Constructor options for {@link TypeParameter}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface ITypeParameterOptions {
|
||||
constraintExcerpt: Excerpt;
|
||||
defaultTypeExcerpt: Excerpt;
|
||||
isOptional: boolean;
|
||||
name: string;
|
||||
parent: ApiTypeParameterListMixin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a named type parameter for a generic declaration.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* `TypeParameter` represents a TypeScript declaration such as `T` in this example:
|
||||
*
|
||||
* ```ts
|
||||
* interface IIdentifier {
|
||||
* getCode(): string;
|
||||
* }
|
||||
*
|
||||
* class BarCode implements IIdentifier {
|
||||
* private _value: number;
|
||||
* public getCode(): string { return this._value.toString(); }
|
||||
* }
|
||||
*
|
||||
* class Book<TIdentifier extends IIdentifier = BarCode> {
|
||||
* public identifier: TIdentifier;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* `TypeParameter` objects belong to the {@link (ApiTypeParameterListMixin:interface).typeParameters} collection.
|
||||
* @public
|
||||
*/
|
||||
export class TypeParameter {
|
||||
/**
|
||||
* An {@link Excerpt} that describes the base constraint of the type parameter.
|
||||
*
|
||||
* @remarks
|
||||
* In the example below, the `constraintExcerpt` would correspond to the `IIdentifier` subexpression:
|
||||
*
|
||||
* ```ts
|
||||
* class Book<TIdentifier extends IIdentifier = BarCode> {
|
||||
* public identifier: TIdentifier;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public readonly constraintExcerpt: Excerpt;
|
||||
|
||||
/**
|
||||
* An {@link Excerpt} that describes the default type of the type parameter.
|
||||
*
|
||||
* @remarks
|
||||
* In the example below, the `defaultTypeExcerpt` would correspond to the `BarCode` subexpression:
|
||||
*
|
||||
* ```ts
|
||||
* class Book<TIdentifier extends IIdentifier = BarCode> {
|
||||
* public identifier: TIdentifier;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
public readonly defaultTypeExcerpt: Excerpt;
|
||||
|
||||
/**
|
||||
* The parameter name.
|
||||
*/
|
||||
public name: string;
|
||||
|
||||
/**
|
||||
* Whether the type parameter is optional. True IFF there exists a `defaultTypeExcerpt`.
|
||||
*/
|
||||
public isOptional: boolean;
|
||||
|
||||
private readonly _parent: ApiTypeParameterListMixin;
|
||||
|
||||
public constructor(options: ITypeParameterOptions) {
|
||||
this.name = options.name;
|
||||
this.constraintExcerpt = options.constraintExcerpt;
|
||||
this.defaultTypeExcerpt = options.defaultTypeExcerpt;
|
||||
this.isOptional = options.isOptional;
|
||||
this._parent = options.parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `@typeParam` documentation for this parameter, if present.
|
||||
*/
|
||||
public get tsdocTypeParamBlock(): tsdoc.DocParamBlock | undefined {
|
||||
if (this._parent instanceof ApiDocumentedItem && this._parent.tsdocComment) {
|
||||
return this._parent.tsdocComment.typeParams.tryGetBlockByName(this.name);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user