181 lines
8.0 KiB
JavaScript
181 lines
8.0 KiB
JavaScript
|
"use strict";
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
|
||
|
// See LICENSE in the project root for license information.
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.ModelReferenceResolver = void 0;
|
||
|
const tsdoc_1 = require("@microsoft/tsdoc");
|
||
|
const ApiItem_1 = require("../items/ApiItem");
|
||
|
const ApiItemContainerMixin_1 = require("../mixins/ApiItemContainerMixin");
|
||
|
const ApiParameterListMixin_1 = require("../mixins/ApiParameterListMixin");
|
||
|
/**
|
||
|
* 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.
|
||
|
*/
|
||
|
class ModelReferenceResolver {
|
||
|
constructor(apiModel) {
|
||
|
this._apiModel = apiModel;
|
||
|
}
|
||
|
resolve(declarationReference, contextApiItem) {
|
||
|
const result = {
|
||
|
resolvedApiItem: undefined,
|
||
|
errorMessage: undefined
|
||
|
};
|
||
|
let apiPackage = undefined;
|
||
|
// Is this an absolute reference?
|
||
|
if (declarationReference.packageName !== undefined) {
|
||
|
apiPackage = this._apiModel.tryGetPackageByName(declarationReference.packageName);
|
||
|
if (apiPackage === undefined) {
|
||
|
result.errorMessage = `The package "${declarationReference.packageName}" could not be located`;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
// 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;
|
||
|
}
|
||
|
}
|
||
|
const importPath = declarationReference.importPath || '';
|
||
|
const foundEntryPoints = apiPackage.findEntryPointsByPath(importPath);
|
||
|
if (foundEntryPoints.length !== 1) {
|
||
|
result.errorMessage = `The import path "${importPath}" could not be resolved`;
|
||
|
return result;
|
||
|
}
|
||
|
let currentItem = 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 = memberReference.memberIdentifier.identifier;
|
||
|
if (!ApiItemContainerMixin_1.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 = currentItem.findMembersByName(identifier);
|
||
|
if (foundMembers.length === 0) {
|
||
|
result.errorMessage = `The member reference ${JSON.stringify(identifier)} was not found`;
|
||
|
return result;
|
||
|
}
|
||
|
const memberSelector = 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;
|
||
|
switch (memberSelector.selectorKind) {
|
||
|
case tsdoc_1.SelectorKind.System:
|
||
|
memberSelectorResult = this._selectUsingSystemSelector(foundMembers, memberSelector, identifier);
|
||
|
break;
|
||
|
case tsdoc_1.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;
|
||
|
}
|
||
|
_selectUsingSystemSelector(foundMembers, memberSelector, identifier) {
|
||
|
const result = {
|
||
|
resolvedApiItem: undefined,
|
||
|
errorMessage: undefined
|
||
|
};
|
||
|
const selectorName = memberSelector.selector;
|
||
|
let selectorItemKind;
|
||
|
switch (selectorName) {
|
||
|
case 'class':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.Class;
|
||
|
break;
|
||
|
case 'enum':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.Enum;
|
||
|
break;
|
||
|
case 'function':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.Function;
|
||
|
break;
|
||
|
case 'interface':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.Interface;
|
||
|
break;
|
||
|
case 'namespace':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.Namespace;
|
||
|
break;
|
||
|
case 'type':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.TypeAlias;
|
||
|
break;
|
||
|
case 'variable':
|
||
|
selectorItemKind = ApiItem_1.ApiItemKind.Variable;
|
||
|
break;
|
||
|
default:
|
||
|
result.errorMessage = `Unsupported system selector "${selectorName}"`;
|
||
|
return result;
|
||
|
}
|
||
|
const matches = 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;
|
||
|
}
|
||
|
_selectUsingIndexSelector(foundMembers, memberSelector, identifier) {
|
||
|
const result = {
|
||
|
resolvedApiItem: undefined,
|
||
|
errorMessage: undefined
|
||
|
};
|
||
|
const selectedMembers = [];
|
||
|
const selectorOverloadIndex = parseInt(memberSelector.selector, 10);
|
||
|
for (const foundMember of foundMembers) {
|
||
|
if (ApiParameterListMixin_1.ApiParameterListMixin.isBaseClassOf(foundMember)) {
|
||
|
if (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;
|
||
|
}
|
||
|
}
|
||
|
exports.ModelReferenceResolver = ModelReferenceResolver;
|
||
|
//# sourceMappingURL=ModelReferenceResolver.js.map
|