Skip to content

Commit

Permalink
Merge pull request #1406 from microsoft/octogonz/ae-new-canonical-ref…
Browse files Browse the repository at this point in the history
…erences

[api-extractor] Add an experimental new ApiItem.canonicalReference property
  • Loading branch information
octogonz authored Aug 8, 2019
2 parents 4e83f6b + 55a4264 commit 16c6a7e
Show file tree
Hide file tree
Showing 55 changed files with 762 additions and 237 deletions.
2 changes: 1 addition & 1 deletion apps/api-documenter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"@microsoft/api-extractor-model": "7.3.0",
"@microsoft/node-core-library": "3.13.0",
"@microsoft/ts-command-line": "4.2.6",
"@microsoft/tsdoc": "0.12.10",
"@microsoft/tsdoc": "0.12.12",
"colors": "~1.2.1",
"js-yaml": "~3.13.1"
},
Expand Down
2 changes: 1 addition & 1 deletion apps/api-extractor-model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
},
"dependencies": {
"@microsoft/node-core-library": "3.13.0",
"@microsoft/tsdoc": "0.12.10",
"@microsoft/tsdoc": "0.12.12",
"@types/node": "8.5.8"
},
"devDependencies": {
Expand Down
62 changes: 54 additions & 8 deletions apps/api-extractor-model/src/items/ApiItem.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
// 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/beta/DeclarationReference';
import { Constructor, PropertiesOf } from '../mixins/Mixin';
import { ApiPackage } from '../model/ApiPackage';
import { ApiParameterListMixin } from '../mixins/ApiParameterListMixin';
import { DeserializerContext } from '../model/DeserializerContext';
import { InternalError } from '@microsoft/node-core-library';

/**
* The type returned by the {@link ApiItem.kind} property, which can be used to easily distinguish subclasses of
Expand Down Expand Up @@ -44,14 +46,13 @@ export interface IApiItemOptions {

export interface IApiItemJson {
kind: ApiItemKind;
canonicalReference: string;
}

/**
* PRIVATE
* Allows ApiItemContainerMixin to assign the parent.
*/
// PRIVATE - Allows ApiItemContainerMixin to assign the parent.
//
// tslint:disable-next-line:variable-name
export const ApiItem_parent: unique symbol = Symbol('ApiItem._parent');
export const ApiItem_onParentChanged: unique symbol = Symbol('ApiItem._onAddToContainer');

/**
* The abstract base class for all members of an `ApiModel` object.
Expand All @@ -62,7 +63,8 @@ export const ApiItem_parent: unique symbol = Symbol('ApiItem._parent');
* @public
*/
export class ApiItem {
public [ApiItem_parent]: ApiItem | undefined;
private _canonicalReference: DeclarationReference | undefined;
private _parent: ApiItem | undefined;

public static deserialize(jsonObject: IApiItemJson, context: DeserializerContext): ApiItem {
// The Deserializer class is coupled with a ton of other classes, so we delay loading it
Expand All @@ -84,6 +86,7 @@ export class ApiItem {
/** @virtual */
public serializeInto(jsonObject: Partial<IApiItemJson>): void {
jsonObject.kind = this.kind;
jsonObject.canonicalReference = this.canonicalReference.toString();
}

/**
Expand All @@ -94,6 +97,28 @@ export class ApiItem {
throw new Error('ApiItem.kind was not implemented by the child class');
}

/**
* Warning: This API is used internally by API extractor but is not yet ready for general usage.
*
* @remarks
*
* Returns a `DeclarationReference` object using the experimental new declaration reference notation.
*
* @beta
*/
public get canonicalReference(): DeclarationReference {
if (!this._canonicalReference) {
try {
this._canonicalReference = this.buildCanonicalReference();
} catch (e) {
const name: string = this.getScopedNameWithinPackage() || this.displayName;
throw new InternalError(`Error building canonical reference for ${name}:\n`
+ e.message);
}
}
return this._canonicalReference;
}

/**
* Returns a string key that can be used to efficiently retrieve an `ApiItem` from an `ApiItemContainerMixin`.
* The key is unique within the container. Its format is undocumented and may change at any time.
Expand All @@ -105,7 +130,7 @@ export class ApiItem {
* @virtual
*/
public get containerKey(): string {
throw new Error('ApiItem.containerKey was not implemented by the child class');
throw new InternalError('ApiItem.containerKey was not implemented by the child class');
}

/**
Expand Down Expand Up @@ -135,7 +160,7 @@ export class ApiItem {
* @virtual
*/
public get parent(): ApiItem | undefined {
return this[ApiItem_parent];
return this._parent;
}

/**
Expand Down Expand Up @@ -215,6 +240,27 @@ export class ApiItem {
public getSortKey(): string {
return this.containerKey;
}

/**
* PRIVATE
*
* @privateRemarks
* Allows ApiItemContainerMixin to assign the parent when the item is added to a container.
*
* @internal
*/
public [ApiItem_onParentChanged](parent: ApiItem | undefined): void {
this._parent = parent;
this._canonicalReference = undefined;
}

/**
* Builds the cached object used by the `canonicalReference` property.
* @virtual
*/
protected buildCanonicalReference(): DeclarationReference {
throw new InternalError('ApiItem.canonicalReference was not implemented by the child class');
}
}

/**
Expand Down
6 changes: 3 additions & 3 deletions apps/api-extractor-model/src/mixins/ApiItemContainerMixin.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.s

import { ApiItem, ApiItem_parent, IApiItemJson, IApiItemOptions, IApiItemConstructor } from '../items/ApiItem';
import { ApiItem, ApiItem_onParentChanged, IApiItemJson, IApiItemOptions, IApiItemConstructor } from '../items/ApiItem';
import { ApiNameMixin } from './ApiNameMixin';
import { DeserializerContext } from '../model/DeserializerContext';

Expand Down Expand Up @@ -135,7 +135,7 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(ba
throw new Error('Another member has already been added with the same name and containerKey');
}

const existingParent: ApiItem | undefined = member[ApiItem_parent];
const existingParent: ApiItem | undefined = member.parent;
if (existingParent !== undefined) {
throw new Error(`This item has already been added to another container: "${existingParent.displayName}"`);
}
Expand All @@ -145,7 +145,7 @@ export function ApiItemContainerMixin<TBaseClass extends IApiItemConstructor>(ba
this[_membersSorted] = false;
this[_membersByContainerKey].set(member.containerKey, member);

member[ApiItem_parent] = this;
member[ApiItem_onParentChanged](this);
}

public tryGetMemberByKey(containerKey: string): ApiItem | undefined {
Expand Down
12 changes: 12 additions & 0 deletions apps/api-extractor-model/src/model/ApiCallSignature.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem';
import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin';
Expand Down Expand Up @@ -69,4 +70,15 @@ export class ApiCallSignature extends ApiTypeParameterListMixin(ApiParameterList
public get containerKey(): string {
return ApiCallSignature.getContainerKey(this.overloadIndex);
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const parent: DeclarationReference = this.parent
? this.parent.canonicalReference
// .withMeaning() requires some kind of component
: DeclarationReference.empty().addNavigationStep(Navigation.Members, '(parent)');
return parent
.withMeaning(Meaning.CallSignature)
.withOverloadIndex(this.overloadIndex);
}
}
9 changes: 9 additions & 0 deletions apps/api-extractor-model/src/model/ApiClass.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation, Component } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { ApiDeclaredItem, IApiDeclaredItemOptions, IApiDeclaredItemJson } from '../items/ApiDeclaredItem';
import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin';
Expand Down Expand Up @@ -116,4 +117,12 @@ export class ApiClass extends ApiItemContainerMixin(ApiNameMixin(ApiTypeParamete

jsonObject.implementsTokenRanges = this.implementsTypes.map(x => x.excerpt.tokenRange);
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
.addNavigationStep(Navigation.Exports, nameComponent)
.withMeaning(Meaning.Class);
}
}
12 changes: 12 additions & 0 deletions apps/api-extractor-model/src/model/ApiConstructSignature.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem';
import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin';
Expand Down Expand Up @@ -82,4 +83,15 @@ export class ApiConstructSignature extends ApiTypeParameterListMixin(ApiParamete
public get containerKey(): string {
return ApiConstructSignature.getContainerKey(this.overloadIndex);
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const parent: DeclarationReference = this.parent
? this.parent.canonicalReference
// .withMeaning() requires some kind of component
: DeclarationReference.empty().addNavigationStep(Navigation.Members, '(parent)');
return parent
.withMeaning(Meaning.ConstructSignature)
.withOverloadIndex(this.overloadIndex);
}
}
12 changes: 12 additions & 0 deletions apps/api-extractor-model/src/model/ApiConstructor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem';
import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin';
Expand Down Expand Up @@ -62,4 +63,15 @@ export class ApiConstructor extends ApiParameterListMixin(ApiReleaseTagMixin(Api
public get containerKey(): string {
return ApiConstructor.getContainerKey(this.overloadIndex);
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const parent: DeclarationReference = this.parent
? this.parent.canonicalReference
// .withMeaning() requires some kind of component
: DeclarationReference.empty().addNavigationStep(Navigation.Members, '(parent)');
return parent
.withMeaning(Meaning.Constructor)
.withOverloadIndex(this.overloadIndex);
}
}
35 changes: 33 additions & 2 deletions apps/api-extractor-model/src/model/ApiEntryPoint.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// 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/beta/DeclarationReference';
import { ApiItem, ApiItemKind } from '../items/ApiItem';
import { ApiItemContainerMixin, IApiItemContainerMixinOptions } from '../mixins/ApiItemContainerMixin';
import { IApiNameMixinOptions, ApiNameMixin } from '../mixins/ApiNameMixin';
import { ApiPackage } from './ApiPackage';

/**
* Constructor options for {@link ApiEntryPoint}.
Expand All @@ -20,8 +22,11 @@ export interface IApiEntryPointOptions extends IApiItemContainerMixinOptions, IA
* 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. For example, suppose the package.json file
* looks like this:
* `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
* {
Expand Down Expand Up @@ -51,4 +56,30 @@ export class ApiEntryPoint extends ApiItemContainerMixin(ApiNameMixin(ApiItem))
// 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 buildCanonicalReference(): DeclarationReference {

if (this.parent instanceof ApiPackage) {
return DeclarationReference.package(this.parent.name, this.importPath);
}

return DeclarationReference.empty();
}
}
9 changes: 9 additions & 0 deletions apps/api-extractor-model/src/model/ApiEnum.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation, Component } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { ApiDeclaredItem, IApiDeclaredItemOptions } from '../items/ApiDeclaredItem';
import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin';
Expand Down Expand Up @@ -71,4 +72,12 @@ export class ApiEnum extends ApiItemContainerMixin(ApiNameMixin(ApiReleaseTagMix
}
super.addMember(member);
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
.addNavigationStep(Navigation.Exports, nameComponent)
.withMeaning(Meaning.Enum);
}
}
9 changes: 9 additions & 0 deletions apps/api-extractor-model/src/model/ApiEnumMember.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation, Component } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { ApiDeclaredItem, IApiDeclaredItemOptions, IApiDeclaredItemJson } from '../items/ApiDeclaredItem';
import { ApiReleaseTagMixin, IApiReleaseTagMixinOptions } from '../mixins/ApiReleaseTagMixin';
Expand Down Expand Up @@ -86,4 +87,12 @@ export class ApiEnumMember extends ApiNameMixin(ApiReleaseTagMixin(ApiDeclaredIt

jsonObject.initializerTokenRange = this.initializerExcerpt.tokenRange;
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
.addNavigationStep(Navigation.Exports, nameComponent)
.withMeaning(Meaning.Member);
}
}
10 changes: 10 additions & 0 deletions apps/api-extractor-model/src/model/ApiFunction.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import { DeclarationReference, Meaning, Navigation, Component } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
import { ApiItemKind } from '../items/ApiItem';
import { IApiDeclaredItemOptions, ApiDeclaredItem } from '../items/ApiDeclaredItem';
import { IApiParameterListMixinOptions, ApiParameterListMixin } from '../mixins/ApiParameterListMixin';
Expand Down Expand Up @@ -63,4 +64,13 @@ export class ApiFunction extends ApiNameMixin(ApiTypeParameterListMixin(ApiParam
public get containerKey(): string {
return ApiFunction.getContainerKey(this.name, this.overloadIndex);
}

/** @beta @override */
public buildCanonicalReference(): DeclarationReference {
const nameComponent: Component = DeclarationReference.parseComponent(this.name);
return (this.parent ? this.parent.canonicalReference : DeclarationReference.empty())
.addNavigationStep(Navigation.Exports, nameComponent)
.withMeaning(Meaning.Function)
.withOverloadIndex(this.overloadIndex);
}
}
Loading

0 comments on commit 16c6a7e

Please sign in to comment.