Skip to content

Commit b204f6a

Browse files
committed
fix!: Don't use TS internal symbol IDs
They are unreliable when resolving imports, so while it worked perfectly fine when TypeDoc didn't do that, now it started breaking things.
1 parent 62314da commit b204f6a

39 files changed

+1161
-769
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ If you would like to improve the documentation in any of these areas, please ope
5959
Unsure of where to begin contributing to TypeDoc? You can start by looking through the issues labeled [good-first-issue] and [help-wanted]. Issues labeled with [good-first-issue] should only require changing a few lines of code and a test or two. Issues labeled with [help-wanted] can be considerably more involved and may require changing multiple files.
6060

6161
For instructions on setting up your environment, see the [setup](#setup---git-github-and-node) instructions in this document.
62+
Once set up, you may find the [development](https://typedoc.org/guides/development/) page useful for an overview of TypeDoc's architecture.
6263

6364
If you have started work on an issue and get stuck or want a second opinion on your implementation feel free to reach out through [Gitter].
6465

scripts/rebuild_specs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ async function main(command = 'all', filter = '') {
127127
if (!stat.isDirectory()) return false;
128128
return fullPath.endsWith(filter);
129129
}).map(([path]) => path));
130+
} else if (filter !== '') {
131+
console.warn('Specifying a filter when rebuilding render specs only has no effect.');
130132
}
131133

132134
if (['all', 'renderer'].includes(command)) {

src/lib/converter/context.ts

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ export class Context {
7373
inheritParent?: ts.Node;
7474

7575
/**
76-
* List symbol ids of inherited children already visited while inheriting.
76+
* List symbol fqns of inherited children already visited while inheriting.
7777
*/
78-
inheritedChildren?: number[];
78+
inheritedChildren?: string[];
7979

8080
/**
8181
* The names of the children of the scope before inheritance has been started.
@@ -87,11 +87,6 @@ export class Context {
8787
*/
8888
visitStack: ts.Node[];
8989

90-
/**
91-
* Next free symbol id used by [[getSymbolID]].
92-
*/
93-
private symbolID = -1024;
94-
9590
/**
9691
* The pattern that should be used to flag external source files.
9792
*/
@@ -159,6 +154,12 @@ export class Context {
159154
return symbol;
160155
}
161156

157+
resolveAliasedSymbol(symbol: ts.Symbol): ts.Symbol;
158+
resolveAliasedSymbol(symbol: ts.Symbol | undefined): ts.Symbol | undefined;
159+
resolveAliasedSymbol(symbol: ts.Symbol | undefined) {
160+
return (symbol && ts.SymbolFlags.Alias & symbol.flags) ? this.checker.getAliasedSymbol(symbol) : symbol;
161+
}
162+
162163
/**
163164
* Return the current logger instance.
164165
*
@@ -169,40 +170,18 @@ export class Context {
169170
}
170171

171172
/**
172-
* Return the symbol id of the given symbol.
173-
*
174-
* The compiler sometimes does not assign an id to symbols, this method makes sure that we have one.
175-
* It will assign negative ids if they are not set.
176-
*
177-
* @param symbol The symbol whose id should be returned.
178-
* @returns The id of the given symbol or undefined if no symbol is provided.
179-
*/
180-
getSymbolID(symbol: ts.Symbol | undefined): number | undefined {
181-
if (!symbol) {
182-
return;
183-
}
184-
if (!symbol.id) {
185-
symbol.id = this.symbolID--;
186-
}
187-
return symbol.id;
188-
}
189-
190-
/**
191-
* Register a newly generated reflection.
192-
*
193-
* Ensures that the reflection is both listed in [[Project.reflections]] and
194-
* [[Project.symbolMapping]] if applicable.
173+
* Register a newly generated reflection. All created reflections should be
174+
* passed to this method to ensure that the project helper functions work correctly.
195175
*
196176
* @param reflection The reflection that should be registered.
197177
* @param node The node the given reflection was resolved from.
198178
* @param symbol The symbol the given reflection was resolved from.
199179
*/
200-
registerReflection(reflection: Reflection, node?: ts.Node, symbol?: ts.Symbol) {
201-
this.project.reflections[reflection.id] = reflection;
202-
203-
const id = this.getSymbolID(symbol ? symbol : (node ? node.symbol : undefined));
204-
if (!this.isInherit && id && !this.project.symbolMapping[id]) {
205-
this.project.symbolMapping[id] = reflection.id;
180+
registerReflection(reflection: Reflection, symbol?: ts.Symbol) {
181+
if (symbol) {
182+
this.project.registerReflection(reflection, this.checker.getFullyQualifiedName(symbol));
183+
} else {
184+
this.project.registerReflection(reflection);
206185
}
207186
}
208187

@@ -329,7 +308,7 @@ export class Context {
329308
}
330309

331310
if (baseNode.symbol) {
332-
const id = this.getSymbolID(baseNode.symbol)!;
311+
const id = this.checker.getFullyQualifiedName(baseNode.symbol);
333312
if (this.inheritedChildren && this.inheritedChildren.includes(id)) {
334313
return target;
335314
} else {

src/lib/converter/converter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ export class Converter extends ChildableComponent<Application, ConverterComponen
290290
if (dangling.length) {
291291
this.owner.logger.warn([
292292
'Some names refer to reflections that do not exist.',
293-
'This can be caused by exporting a reflection only under a different name than it is declared',
294-
'or by a plugin removing reflections. The names that cannot be resolved are:',
293+
'This can be caused by exporting a reflection only under a different name than it is declared when excludeNotExported is set',
294+
'or by a plugin removing reflections without removing references. The names that cannot be resolved are:',
295295
...dangling
296296
].join('\n'));
297297
}

src/lib/converter/factories/declaration.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export function createDeclaration(context: Context, node: ts.Declaration, kind:
142142

143143
if (child) {
144144
children.push(child);
145-
context.registerReflection(child, node);
145+
context.registerReflection(child, context.getSymbolAtLocation(node) ?? node.symbol);
146146
}
147147
} else {
148148
// Merge the existent reflection with the given node

src/lib/converter/factories/parameter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export function createParameter(context: Context, node: ts.ParameterDeclaration)
2424

2525
const parameter = new ParameterReflection(node.symbol.name, ReflectionKind.Parameter, signature);
2626
parameter.flags.setFlag(ReflectionFlag.Exported, context.scope.flags.isExported);
27-
context.registerReflection(parameter, node);
27+
context.registerReflection(parameter);
2828
context.withScope(parameter, () => {
2929
if (ts.isArrayBindingPattern(node.name) || ts.isObjectBindingPattern(node.name)) {
3030
parameter.type = context.converter.convertType(context, node.name);

src/lib/converter/factories/reference.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,27 @@ export function createReferenceType(context: Context, symbol: ts.Symbol | undefi
1919
}
2020

2121
const checker = context.checker;
22-
const id = context.getSymbolID(symbol)!;
2322
let name = checker.symbolToString(symbol);
2423

2524
if (includeParent && symbol.parent) {
2625
name = checker.symbolToString(symbol.parent) + '.' + name;
2726
}
2827

29-
return new ReferenceType(name, id);
28+
return new ReferenceType(name, context.checker.getFullyQualifiedName(symbol));
3029
}
3130

3231
export function createReferenceReflection(context: Context, source: ts.Symbol, target: ts.Symbol): ReferenceReflection {
3332
if (!(context.scope instanceof ContainerReflection)) {
3433
throw new Error('Cannot add reference to a non-container');
3534
}
3635

37-
const reflection = new ReferenceReflection(source.name, [ReferenceState.Unresolved, context.getSymbolID(target)!], context.scope);
36+
const reflection = new ReferenceReflection(source.name, [ReferenceState.Unresolved, context.checker.getFullyQualifiedName(target)], context.scope);
3837
reflection.flags.setFlag(ReflectionFlag.Exported, true); // References are exported by necessity
3938
if (!context.scope.children) {
4039
context.scope.children = [];
4140
}
4241
context.scope.children.push(reflection);
43-
context.registerReflection(reflection, undefined, source);
42+
context.registerReflection(reflection, source);
4443
context.trigger(Converter.EVENT_CREATE_DECLARATION, reflection);
4544

4645
return reflection;

src/lib/converter/factories/signature.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { ReflectionKind, SignatureReflection, ContainerReflection, DeclarationRe
44
import { Context } from '../context';
55
import { Converter } from '../converter';
66
import { createParameter } from './parameter';
7-
import { createReferenceType } from './reference';
87

98
/**
109
* Create a new signature reflection from the given node.
@@ -23,7 +22,7 @@ export function createSignature(context: Context, node: ts.SignatureDeclaration,
2322

2423
const signature = new SignatureReflection(name, kind, container);
2524
signature.flags.setFlag(ReflectionFlag.Exported, container.flags.isExported);
26-
context.registerReflection(signature, node);
25+
context.registerReflection(signature);
2726
context.withScope(signature, node.typeParameters, true, () => {
2827
node.parameters.forEach((parameter: ts.ParameterDeclaration) => {
2928
createParameter(context, parameter);
@@ -32,7 +31,7 @@ export function createSignature(context: Context, node: ts.SignatureDeclaration,
3231
signature.type = extractSignatureType(context, node);
3332

3433
if (container.inheritedFrom) {
35-
signature.inheritedFrom = createReferenceType(context, node.symbol, true);
34+
signature.inheritedFrom = container.inheritedFrom.clone();
3635
}
3736
});
3837

src/lib/converter/factories/type-parameter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function createTypeParameter(context: Context, node: ts.TypeParameterDecl
3030
}
3131
reflection.typeParameters.push(typeParameterReflection);
3232

33-
context.registerReflection(typeParameterReflection, node);
33+
context.registerReflection(typeParameterReflection);
3434
context.trigger(Converter.EVENT_CREATE_TYPE_PARAMETER, typeParameterReflection, node);
3535

3636
return typeParameter;

src/lib/converter/nodes/constructor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export class ConstructorConverter extends ConverterNodeComponent<ts.ConstructorD
4141
const signature = createSignature(context, node, name, ReflectionKind.ConstructorSignature);
4242
// If no return type defined, use the parent one.
4343
if (!node.type) {
44-
signature.type = new ReferenceType(parent.name, ReferenceType.SYMBOL_ID_RESOLVED, parent);
44+
signature.type = new ReferenceType(parent.name, ReferenceType.SYMBOL_FQN_RESOLVED, parent);
4545
}
4646
method!.signatures = method!.signatures || [];
4747
method!.signatures.push(signature);

src/lib/converter/nodes/export.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Reflection, ReflectionFlag, DeclarationReflection, ContainerReflection
44
import { Context } from '../context';
55
import { Component, ConverterNodeComponent } from '../components';
66
import { createReferenceReflection } from '../factories/reference';
7+
import { SourceFileMode } from './block';
78

89
@Component({name: 'node:export'})
910
export class ExportConverter extends ConverterNodeComponent<ts.ExportAssignment> {
@@ -30,16 +31,14 @@ export class ExportConverter extends ConverterNodeComponent<ts.ExportAssignment>
3031
if (!declaration.symbol) {
3132
return;
3233
}
33-
const id = project.symbolMapping[context.getSymbolID(declaration.symbol)!];
34-
if (!id) {
35-
return;
36-
}
3734

38-
const reflection = project.reflections[id];
35+
const reflection = project.getReflectionFromFQN(context.checker.getFullyQualifiedName(declaration.symbol));
3936
if (node.isExportEquals && reflection instanceof DeclarationReflection) {
4037
reflection.setFlag(ReflectionFlag.ExportAssignment, true);
4138
}
42-
markAsExported(reflection);
39+
if (reflection) {
40+
markAsExported(reflection);
41+
}
4342
});
4443
}
4544

@@ -60,6 +59,11 @@ export class ExportDeclarationConverter extends ConverterNodeComponent<ts.Export
6059
supports = [ts.SyntaxKind.ExportDeclaration];
6160

6261
convert(context: Context, node: ts.ExportDeclaration): Reflection | undefined {
62+
// It doesn't make sense to convert export declarations if we are pretending everything is global.
63+
if (this.application.options.getValue('mode') === SourceFileMode.File) {
64+
return;
65+
}
66+
6367
const scope = context.scope;
6468
if (!(scope instanceof ContainerReflection)) {
6569
throw new Error('Expected to be within a container');
@@ -68,16 +72,15 @@ export class ExportDeclarationConverter extends ConverterNodeComponent<ts.Export
6872
if (node.exportClause) { // export { a, a as b }
6973
node.exportClause.elements.forEach(specifier => {
7074
const source = context.getSymbolAtLocation(specifier.name);
71-
const target = context.getSymbolAtLocation(specifier.propertyName ?? specifier.name);
75+
const target = context.resolveAliasedSymbol(context.getSymbolAtLocation(specifier.propertyName ?? specifier.name));
7276
if (source && target) {
73-
const original = (target.flags & ts.SymbolFlags.Alias) ? context.checker.getAliasedSymbol(target) : target;
7477
// If the original declaration is in this file, export {} was used with something
7578
// defined in this file and we don't need to create a reference unless the name is different.
7679
if (!node.moduleSpecifier && !specifier.propertyName) {
7780
return;
7881
}
7982

80-
createReferenceReflection(context, source, original);
83+
createReferenceReflection(context, source, target);
8184
}
8285
});
8386
} else if (node.moduleSpecifier) { // export * from ...

src/lib/converter/nodes/variable.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import { Context } from '../context';
77
import { Component, ConverterNodeComponent } from '../components';
88
import { convertDefaultValue } from '../index';
99

10+
type VarNodeType = ts.PropertySignature
11+
| ts.PropertyDeclaration
12+
| ts.PropertyAssignment
13+
| ts.ShorthandPropertyAssignment
14+
| ts.VariableDeclaration
15+
| ts.ImportEqualsDeclaration
16+
| ts.BindingElement;
17+
1018
@Component({name: 'node:variable'})
1119
export class VariableConverter extends ConverterNodeComponent<ts.VariableDeclaration> {
1220
/**
@@ -18,6 +26,7 @@ export class VariableConverter extends ConverterNodeComponent<ts.VariableDeclara
1826
ts.SyntaxKind.PropertyAssignment,
1927
ts.SyntaxKind.ShorthandPropertyAssignment,
2028
ts.SyntaxKind.VariableDeclaration,
29+
ts.SyntaxKind.ImportEqualsDeclaration,
2130
ts.SyntaxKind.BindingElement
2231
];
2332

@@ -36,7 +45,7 @@ export class VariableConverter extends ConverterNodeComponent<ts.VariableDeclara
3645
* @param node The variable declaration node that should be analyzed.
3746
* @return The resulting reflection or NULL.
3847
*/
39-
convert(context: Context, node: ts.VariableDeclaration): Reflection | undefined {
48+
convert(context: Context, node: VarNodeType): Reflection | undefined {
4049
const comment = createComment(node);
4150
if (comment && comment.hasTag('resolve')) {
4251
const resolveType = context.getTypeAtLocation(node);
@@ -84,7 +93,7 @@ export class VariableConverter extends ConverterNodeComponent<ts.VariableDeclara
8493
}
8594

8695
context.withScope(variable, () => {
87-
if (node.initializer) {
96+
if ('initializer' in node && node.initializer) {
8897
switch (node.initializer.kind) {
8998
case ts.SyntaxKind.ArrowFunction:
9099
case ts.SyntaxKind.FunctionExpression:
@@ -99,15 +108,15 @@ export class VariableConverter extends ConverterNodeComponent<ts.VariableDeclara
99108
}
100109
break;
101110
default:
102-
variable!.defaultValue = convertDefaultValue(node);
111+
variable!.defaultValue = convertDefaultValue(node as ts.VariableDeclaration);
103112
}
104113
}
105114

106115
if (variable!.kind === kind || variable!.kind === ReflectionKind.Event) {
107116
if (isBindingPattern) {
108117
variable!.type = this.owner.convertType(context, node.name);
109118
} else {
110-
variable!.type = this.owner.convertType(context, node.type, context.getTypeAtLocation(node));
119+
variable!.type = this.owner.convertType(context, (node as ts.VariableDeclaration).type, context.getTypeAtLocation(node));
111120
}
112121
}
113122
});

0 commit comments

Comments
 (0)