Skip to content

Commit 33f8d0d

Browse files
committed
Remove ref from type params, improve uid generation for locals
1 parent d1f1bfe commit 33f8d0d

6 files changed

Lines changed: 96 additions & 39 deletions

File tree

apps/api-extractor-model/src/items/ApiDeclaredItem.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.s
33

4+
import { DeclarationReference } from '@microsoft/tsdoc/lib/beta/DeclarationReference';
45
import { ApiDocumentedItem, IApiDocumentedItemJson, IApiDocumentedItemOptions } from './ApiDocumentedItem';
56
import { Excerpt, ExcerptToken, IExcerptTokenRange, IExcerptToken } from '../mixins/Excerpt';
67
import { DeserializerContext } from '../model/DeserializerContext';
@@ -47,7 +48,11 @@ export class ApiDeclaredItem extends ApiDocumentedItem {
4748
public constructor(options: IApiDeclaredItemOptions) {
4849
super(options);
4950

50-
this._excerptTokens = options.excerptTokens.map(x => ExcerptToken.fromJSON(x));
51+
this._excerptTokens = options.excerptTokens.map(x => {
52+
const canonicalReference: DeclarationReference | undefined = x.canonicalReference === undefined ? undefined :
53+
DeclarationReference.parse(x.canonicalReference);
54+
return new ExcerptToken(x.kind, x.text, canonicalReference);
55+
});
5156
this._excerpt = new Excerpt(this.excerptTokens, { startIndex: 0, endIndex: this.excerptTokens.length });
5257
}
5358

@@ -99,7 +104,13 @@ export class ApiDeclaredItem extends ApiDocumentedItem {
99104
/** @override */
100105
public serializeInto(jsonObject: Partial<IApiDeclaredItemJson>): void {
101106
super.serializeInto(jsonObject);
102-
jsonObject.excerptTokens = this.excerptTokens.map(x => x.toJSON());
107+
jsonObject.excerptTokens = this.excerptTokens.map(x => {
108+
const excerptToken: IExcerptToken = { kind: x.kind, text: x.text };
109+
if (x.canonicalReference !== undefined) {
110+
excerptToken.canonicalReference = x.canonicalReference.toString();
111+
}
112+
return excerptToken;
113+
});
103114
}
104115

105116
/**

apps/api-extractor-model/src/mixins/Excerpt.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,20 +52,6 @@ export class ExcerptToken {
5252
public get canonicalReference(): DeclarationReference | undefined {
5353
return this._canonicalReference;
5454
}
55-
56-
public static fromJSON(object: IExcerptToken): ExcerptToken {
57-
const canonicalReference: DeclarationReference | undefined = object.canonicalReference === undefined ? undefined :
58-
DeclarationReference.parse(object.canonicalReference);
59-
return new ExcerptToken(object.kind, object.text, canonicalReference);
60-
}
61-
62-
public toJSON(): IExcerptToken {
63-
const excerptToken: IExcerptToken = { kind: this.kind, text: this.text };
64-
if (this._canonicalReference !== undefined) {
65-
excerptToken.canonicalReference = this._canonicalReference.toString();
66-
}
67-
return excerptToken;
68-
}
6955
}
7056

7157
/**

apps/api-extractor/src/generators/DeclarationReferenceGenerator.ts

Lines changed: 78 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,27 @@ export class DeclarationReferenceGenerator {
7272
return new DeclarationReference(this._sourceFileToModuleSource(sourceFile));
7373
}
7474

75+
// Do not generate a declaration reference for a type parameter.
7576
if (symbol.flags & ts.SymbolFlags.TypeParameter) {
76-
return DeclarationReference.parse(DeclarationReference.escapeComponentString(symbol.name));
77+
return undefined;
7778
}
7879

7980
const parent: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol);
80-
const parentRef: DeclarationReference | undefined = parent
81-
? this._symbolToDeclarationReference(parent, ts.SymbolFlags.Namespace, /*includeModuleSymbols*/ true)
82-
: new DeclarationReference(GlobalSource.instance);
81+
let parentRef: DeclarationReference | undefined;
82+
if (parent) {
83+
parentRef = this._symbolToDeclarationReference(parent, ts.SymbolFlags.Namespace, /*includeModuleSymbols*/ true);
84+
} else {
85+
// this may be a local symbol in a module...
86+
const sourceFile: ts.SourceFile | undefined =
87+
symbol.declarations
88+
&& symbol.declarations[0]
89+
&& symbol.declarations[0].getSourceFile();
90+
if (ts.isExternalModule(sourceFile)) {
91+
parentRef = new DeclarationReference(this._sourceFileToModuleSource(sourceFile));
92+
} else {
93+
parentRef = new DeclarationReference(GlobalSource.instance);
94+
}
95+
}
8396

8497
if (parentRef === undefined) {
8598
return undefined;
@@ -108,9 +121,13 @@ export class DeclarationReferenceGenerator {
108121
}
109122
}
110123

111-
const navigation: Navigation = isTypeMemberOrNonStaticClassMember(symbol)
112-
? Navigation.Members
113-
: Navigation.Exports;
124+
let navigation: Navigation | 'global' = getNavigationToSymbol(symbol);
125+
if (navigation === 'global') {
126+
if (parentRef.source !== GlobalSource.instance) {
127+
parentRef = new DeclarationReference(GlobalSource.instance);
128+
}
129+
navigation = Navigation.Exports;
130+
}
114131

115132
return parentRef
116133
.addNavigationStep(navigation, localName)
@@ -144,17 +161,64 @@ function isExternalModuleSymbol(symbol: ts.Symbol): boolean {
144161
&& ts.isSourceFile(symbol.valueDeclaration);
145162
}
146163

147-
function isTypeMemberOrNonStaticClassMember(symbol: ts.Symbol): boolean {
164+
function isSameSymbol(left: ts.Symbol | undefined, right: ts.Symbol): boolean {
165+
return left === right
166+
|| !!(left && left.valueDeclaration && right.valueDeclaration && left.valueDeclaration === right.valueDeclaration);
167+
}
168+
169+
function getNavigationToSymbol(symbol: ts.Symbol): Navigation | 'global' {
170+
const parent: ts.Symbol | undefined = TypeScriptInternals.getSymbolParent(symbol);
171+
// First, try to determine navigation to symbol via its parent.
172+
if (parent) {
173+
if (parent.exports && isSameSymbol(parent.exports.get(symbol.escapedName), symbol)) {
174+
return Navigation.Exports;
175+
}
176+
if (parent.members && isSameSymbol(parent.members.get(symbol.escapedName), symbol)) {
177+
return Navigation.Members;
178+
}
179+
if (parent.globalExports && isSameSymbol(parent.globalExports.get(symbol.escapedName), symbol)) {
180+
return 'global';
181+
}
182+
}
183+
184+
// Next, try determining navigation to symbol by its node
148185
if (symbol.valueDeclaration) {
149-
if (ts.isClassLike(symbol.valueDeclaration.parent)) {
150-
return ts.isClassElement(symbol.valueDeclaration)
151-
&& !(ts.getCombinedModifierFlags(symbol.valueDeclaration) & ts.ModifierFlags.Static);
186+
const declaration: ts.Declaration = ts.isBindingElement(symbol.valueDeclaration)
187+
? ts.walkUpBindingElementsAndPatterns(symbol.valueDeclaration)
188+
: symbol.valueDeclaration;
189+
if (ts.isClassElement(declaration) && ts.isClassLike(declaration.parent)) {
190+
// class members are an "export" if they have the static modifier.
191+
return ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Static
192+
? Navigation.Exports
193+
: Navigation.Members;
194+
}
195+
if (ts.isTypeElement(declaration) || ts.isObjectLiteralElement(declaration)) {
196+
// type and object literal element members are just members
197+
return Navigation.Members;
198+
}
199+
if (ts.isEnumMember(declaration)) {
200+
// enum members are exports
201+
return Navigation.Exports;
202+
}
203+
if (ts.isExportSpecifier(declaration)
204+
|| ts.isExportAssignment(declaration)
205+
|| ts.isExportSpecifier(declaration)
206+
|| ts.isExportDeclaration(declaration)
207+
|| ts.isNamedExports(declaration)
208+
) {
209+
return Navigation.Exports;
210+
}
211+
// declarations are exports if they have an `export` modifier.
212+
if (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) {
213+
return Navigation.Exports;
152214
}
153-
if (ts.isInterfaceDeclaration(symbol.valueDeclaration.parent)) {
154-
return ts.isTypeElement(symbol.valueDeclaration);
215+
if (ts.isSourceFile(declaration.parent) && !ts.isExternalModule(declaration.parent)) {
216+
// declarations in a source file are global if the source file is not a module.
217+
return 'global';
155218
}
156219
}
157-
return false;
220+
// all other declarations are locals
221+
return Navigation.Locals;
158222
}
159223

160224
function getMeaningOfSymbol(symbol: ts.Symbol, meaning: ts.SymbolFlags): Meaning | undefined {

build-tests/api-documenter-test/etc/api-documenter-test.api.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1540,17 +1540,15 @@
15401540
},
15411541
{
15421542
"kind": "Reference",
1543-
"text": "T",
1544-
"canonicalReference": "T"
1543+
"text": "T"
15451544
},
15461545
{
15471546
"kind": "Content",
15481547
"text": "): "
15491548
},
15501549
{
15511550
"kind": "Reference",
1552-
"text": "T",
1553-
"canonicalReference": "T"
1551+
"text": "T"
15541552
},
15551553
{
15561554
"kind": "Content",

build-tests/api-extractor-scenarios/etc/test-outputs/typeOf/api-extractor-scenarios.api.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
{
8484
"kind": "Reference",
8585
"text": "ForgottenExport",
86-
"canonicalReference": "!ForgottenExport:class"
86+
"canonicalReference": "api-extractor-scenarios!~ForgottenExport:class"
8787
},
8888
{
8989
"kind": "Content",

build-tests/api-extractor-scenarios/etc/test-outputs/typeParameters/api-extractor-scenarios.api.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -367,8 +367,7 @@
367367
},
368368
{
369369
"kind": "Reference",
370-
"text": "T",
371-
"canonicalReference": "T"
370+
"text": "T"
372371
},
373372
{
374373
"kind": "Content",
@@ -506,8 +505,7 @@
506505
},
507506
{
508507
"kind": "Reference",
509-
"text": "T",
510-
"canonicalReference": "T"
508+
"text": "T"
511509
},
512510
{
513511
"kind": "Content",

0 commit comments

Comments
 (0)