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:
Qjuh
2023-11-07 21:53:36 +01:00
committed by GitHub
parent 95c0b1a59f
commit 5c0fad3b2d
251 changed files with 36017 additions and 296 deletions

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

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

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

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

View 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();
}
}

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

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

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

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

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

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

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

View 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();
}
}

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

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

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

View 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 { 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);
}
}

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

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

View 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)}`);
}
}
}

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

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

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

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

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

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