UNPKG

11 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
3// See LICENSE in the project root for license information.
4var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5 if (k2 === undefined) k2 = k;
6 var desc = Object.getOwnPropertyDescriptor(m, k);
7 if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8 desc = { enumerable: true, get: function() { return m[k]; } };
9 }
10 Object.defineProperty(o, k2, desc);
11}) : (function(o, m, k, k2) {
12 if (k2 === undefined) k2 = k;
13 o[k2] = m[k];
14}));
15var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16 Object.defineProperty(o, "default", { enumerable: true, value: v });
17}) : function(o, v) {
18 o["default"] = v;
19});
20var __importStar = (this && this.__importStar) || function (mod) {
21 if (mod && mod.__esModule) return mod;
22 var result = {};
23 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
24 __setModuleDefault(result, mod);
25 return result;
26};
27Object.defineProperty(exports, "__esModule", { value: true });
28exports.AstReferenceResolver = exports.ResolverFailure = void 0;
29const ts = __importStar(require("typescript"));
30const tsdoc = __importStar(require("@microsoft/tsdoc"));
31const AstSymbol_1 = require("./AstSymbol");
32/**
33 * Used by `AstReferenceResolver` to report a failed resolution.
34 *
35 * @privateRemarks
36 * This class is similar to an `Error` object, but the intent of `ResolverFailure` is to describe
37 * why a reference could not be resolved. This information could be used to throw an actual `Error` object,
38 * but normally it is handed off to the `MessageRouter` instead.
39 */
40class ResolverFailure {
41 constructor(reason) {
42 this.reason = reason;
43 }
44}
45exports.ResolverFailure = ResolverFailure;
46/**
47 * This resolves a TSDoc declaration reference by walking the `AstSymbolTable` compiler state.
48 *
49 * @remarks
50 *
51 * This class is analogous to `ModelReferenceResolver` from the `@microsoft/api-extractor-model` project,
52 * which resolves declaration references by walking the hierarchy loaded from an .api.json file.
53 */
54class AstReferenceResolver {
55 constructor(collector) {
56 this._collector = collector;
57 this._astSymbolTable = collector.astSymbolTable;
58 this._workingPackage = collector.workingPackage;
59 }
60 resolve(declarationReference) {
61 // Is it referring to the working package?
62 if (declarationReference.packageName !== undefined &&
63 declarationReference.packageName !== this._workingPackage.name) {
64 return new ResolverFailure('External package references are not supported');
65 }
66 // Is it a path-based import?
67 if (declarationReference.importPath) {
68 return new ResolverFailure('Import paths are not supported');
69 }
70 const astModule = this._astSymbolTable.fetchAstModuleFromWorkingPackage(this._workingPackage.entryPointSourceFile);
71 if (declarationReference.memberReferences.length === 0) {
72 return new ResolverFailure('Package references are not supported');
73 }
74 const rootMemberReference = declarationReference.memberReferences[0];
75 const exportName = this._getMemberReferenceIdentifier(rootMemberReference);
76 if (exportName instanceof ResolverFailure) {
77 return exportName;
78 }
79 const rootAstEntity = this._astSymbolTable.tryGetExportOfAstModule(exportName, astModule);
80 if (rootAstEntity === undefined) {
81 return new ResolverFailure(`The package "${this._workingPackage.name}" does not have an export "${exportName}"`);
82 }
83 if (!(rootAstEntity instanceof AstSymbol_1.AstSymbol)) {
84 return new ResolverFailure('This type of declaration is not supported yet by the resolver');
85 }
86 let currentDeclaration = this._selectDeclaration(rootAstEntity.astDeclarations, rootMemberReference, rootAstEntity.localName);
87 if (currentDeclaration instanceof ResolverFailure) {
88 return currentDeclaration;
89 }
90 for (let index = 1; index < declarationReference.memberReferences.length; ++index) {
91 const memberReference = declarationReference.memberReferences[index];
92 const memberName = this._getMemberReferenceIdentifier(memberReference);
93 if (memberName instanceof ResolverFailure) {
94 return memberName;
95 }
96 const matchingChildren = currentDeclaration.findChildrenWithName(memberName);
97 if (matchingChildren.length === 0) {
98 return new ResolverFailure(`No member was found with name "${memberName}"`);
99 }
100 const selectedDeclaration = this._selectDeclaration(matchingChildren, memberReference, memberName);
101 if (selectedDeclaration instanceof ResolverFailure) {
102 return selectedDeclaration;
103 }
104 currentDeclaration = selectedDeclaration;
105 }
106 return currentDeclaration;
107 }
108 _getMemberReferenceIdentifier(memberReference) {
109 if (memberReference.memberSymbol !== undefined) {
110 return new ResolverFailure('ECMAScript symbol selectors are not supported');
111 }
112 if (memberReference.memberIdentifier === undefined) {
113 return new ResolverFailure('The member identifier is missing in the root member reference');
114 }
115 return memberReference.memberIdentifier.identifier;
116 }
117 _selectDeclaration(astDeclarations, memberReference, astSymbolName) {
118 const memberSelector = memberReference.selector;
119 if (memberSelector === undefined) {
120 if (astDeclarations.length === 1) {
121 return astDeclarations[0];
122 }
123 else {
124 // If we found multiple matches, but the extra ones are all ancillary declarations,
125 // then return the main declaration.
126 const nonAncillaryMatch = this._tryDisambiguateAncillaryMatches(astDeclarations);
127 if (nonAncillaryMatch) {
128 return nonAncillaryMatch;
129 }
130 return new ResolverFailure(`The reference is ambiguous because "${astSymbolName}"` +
131 ` has more than one declaration; you need to add a TSDoc member reference selector`);
132 }
133 }
134 switch (memberSelector.selectorKind) {
135 case tsdoc.SelectorKind.System:
136 return this._selectUsingSystemSelector(astDeclarations, memberSelector, astSymbolName);
137 case tsdoc.SelectorKind.Index:
138 return this._selectUsingIndexSelector(astDeclarations, memberSelector, astSymbolName);
139 }
140 return new ResolverFailure(`The selector "${memberSelector.selector}" is not a supported selector type`);
141 }
142 _selectUsingSystemSelector(astDeclarations, memberSelector, astSymbolName) {
143 const selectorName = memberSelector.selector;
144 let selectorSyntaxKind;
145 switch (selectorName) {
146 case 'class':
147 selectorSyntaxKind = ts.SyntaxKind.ClassDeclaration;
148 break;
149 case 'enum':
150 selectorSyntaxKind = ts.SyntaxKind.EnumDeclaration;
151 break;
152 case 'function':
153 selectorSyntaxKind = ts.SyntaxKind.FunctionDeclaration;
154 break;
155 case 'interface':
156 selectorSyntaxKind = ts.SyntaxKind.InterfaceDeclaration;
157 break;
158 case 'namespace':
159 selectorSyntaxKind = ts.SyntaxKind.ModuleDeclaration;
160 break;
161 case 'type':
162 selectorSyntaxKind = ts.SyntaxKind.TypeAliasDeclaration;
163 break;
164 case 'variable':
165 selectorSyntaxKind = ts.SyntaxKind.VariableDeclaration;
166 break;
167 default:
168 return new ResolverFailure(`Unsupported system selector "${selectorName}"`);
169 }
170 const matches = astDeclarations.filter((x) => x.declaration.kind === selectorSyntaxKind);
171 if (matches.length === 0) {
172 return new ResolverFailure(`A declaration for "${astSymbolName}" was not found that matches the` +
173 ` TSDoc selector "${selectorName}"`);
174 }
175 if (matches.length > 1) {
176 // If we found multiple matches, but the extra ones are all ancillary declarations,
177 // then return the main declaration.
178 const nonAncillaryMatch = this._tryDisambiguateAncillaryMatches(matches);
179 if (nonAncillaryMatch) {
180 return nonAncillaryMatch;
181 }
182 return new ResolverFailure(`More than one declaration "${astSymbolName}" matches the TSDoc selector "${selectorName}"`);
183 }
184 return matches[0];
185 }
186 _selectUsingIndexSelector(astDeclarations, memberSelector, astSymbolName) {
187 const selectorOverloadIndex = parseInt(memberSelector.selector);
188 const matches = [];
189 for (const astDeclaration of astDeclarations) {
190 const overloadIndex = this._collector.getOverloadIndex(astDeclaration);
191 if (overloadIndex === selectorOverloadIndex) {
192 matches.push(astDeclaration);
193 }
194 }
195 if (matches.length === 0) {
196 return new ResolverFailure(`An overload for "${astSymbolName}" was not found that matches the` +
197 ` TSDoc selector ":${selectorOverloadIndex}"`);
198 }
199 if (matches.length > 1) {
200 // If we found multiple matches, but the extra ones are all ancillary declarations,
201 // then return the main declaration.
202 const nonAncillaryMatch = this._tryDisambiguateAncillaryMatches(matches);
203 if (nonAncillaryMatch) {
204 return nonAncillaryMatch;
205 }
206 return new ResolverFailure(`More than one declaration for "${astSymbolName}" matches the` +
207 ` TSDoc selector ":${selectorOverloadIndex}"`);
208 }
209 return matches[0];
210 }
211 /**
212 * This resolves an ambiguous match in the case where the extra matches are all ancillary declarations,
213 * except for one match that is the main declaration.
214 */
215 _tryDisambiguateAncillaryMatches(matches) {
216 let result = undefined;
217 for (const match of matches) {
218 const declarationMetadata = this._collector.fetchDeclarationMetadata(match);
219 if (!declarationMetadata.isAncillary) {
220 if (result) {
221 return undefined; // more than one match
222 }
223 result = match;
224 }
225 }
226 return result;
227 }
228}
229exports.AstReferenceResolver = AstReferenceResolver;
230//# sourceMappingURL=AstReferenceResolver.js.map
\No newline at end of file