Skip to content

Commit 2625cd0

Browse files
committed
Merge branch 'bodil/master' into beta
Closes #2017
2 parents ac4c4e8 + 1be726c commit 2625cd0

File tree

20 files changed

+1091
-417
lines changed

20 files changed

+1091
-417
lines changed

src/lib/models/types.ts

Lines changed: 49 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
14
import type * as ts from "typescript";
25
import type { Context } from "../converter";
36
import { Reflection } from "./reflections/abstract";
@@ -839,26 +842,30 @@ export class ReferenceType extends Type {
839842
name ?? symbol.name,
840843
symbol,
841844
context.project,
842-
getQualifiedName(context.checker, symbol)
845+
getQualifiedName(symbol, name ?? symbol.name)
843846
);
844847

845848
const symbolPath = symbol?.declarations?.[0]
846849
?.getSourceFile()
847850
.fileName.replace(/\\/g, "/");
848851
if (!symbolPath) return ref;
849852

853+
// Attempt to decide package name from path if it contains "node_modules"
850854
let startIndex = symbolPath.lastIndexOf("node_modules/");
851-
if (startIndex === -1) return ref;
852-
startIndex += "node_modules/".length;
853-
let stopIndex = symbolPath.indexOf("/", startIndex);
854-
// Scoped package, e.g. `@types/node`
855-
if (symbolPath[startIndex] === "@") {
856-
stopIndex = symbolPath.indexOf("/", stopIndex + 1);
855+
if (startIndex !== -1) {
856+
startIndex += "node_modules/".length;
857+
let stopIndex = symbolPath.indexOf("/", startIndex);
858+
// Scoped package, e.g. `@types/node`
859+
if (symbolPath[startIndex] === "@") {
860+
stopIndex = symbolPath.indexOf("/", stopIndex + 1);
861+
}
862+
const packageName = symbolPath.substring(startIndex, stopIndex);
863+
ref.package = packageName;
864+
return ref;
857865
}
858866

859-
const packageName = symbolPath.substring(startIndex, stopIndex);
860-
ref.package = packageName;
861-
867+
// Otherwise, look for a "package.json" file in a parent path
868+
ref.package = findPackageForPath(symbolPath);
862869
return ref;
863870
}
864871

@@ -887,19 +894,14 @@ export class ReferenceType extends Type {
887894
}
888895

889896
override toObject(serializer: Serializer): JSONOutput.ReferenceType {
890-
const result: JSONOutput.ReferenceType = {
897+
return {
891898
type: this.type,
892899
id: this.reflection?.id,
893900
typeArguments: serializer.toObjectsOptional(this.typeArguments),
894901
name: this.name,
902+
qualifiedName: this.qualifiedName,
903+
package: this.package,
895904
};
896-
897-
if (this.package) {
898-
result.qualifiedName = this.qualifiedName;
899-
result.package = this.package;
900-
}
901-
902-
return result;
903905
}
904906
}
905907

@@ -1287,3 +1289,32 @@ export class UnknownType extends Type {
12871289
};
12881290
}
12891291
}
1292+
1293+
const packageJsonLookupCache: Record<string, string> = {};
1294+
1295+
function findPackageForPath(sourcePath: string): string | undefined {
1296+
if (packageJsonLookupCache[sourcePath] !== undefined) {
1297+
return packageJsonLookupCache[sourcePath];
1298+
}
1299+
let basePath = sourcePath;
1300+
for (;;) {
1301+
const nextPath = path.dirname(basePath);
1302+
if (nextPath === basePath) {
1303+
return;
1304+
}
1305+
basePath = nextPath;
1306+
const projectPath = path.join(basePath, "package.json");
1307+
try {
1308+
const packageJsonData = fs.readFileSync(projectPath, {
1309+
encoding: "utf8",
1310+
});
1311+
const packageJson = JSON.parse(packageJsonData);
1312+
if (packageJson.name !== undefined) {
1313+
packageJsonLookupCache[sourcePath] = packageJson.name;
1314+
}
1315+
return packageJson.name;
1316+
} catch (err) {
1317+
continue;
1318+
}
1319+
}
1320+
}

src/lib/serialization/schema.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,11 @@ export interface QueryType extends Type, S<M.QueryType, "type" | "queryType"> {}
241241

242242
export interface ReferenceType
243243
extends Type,
244-
S<M.ReferenceType, "type" | "name" | "typeArguments" | "package"> {
244+
S<
245+
M.ReferenceType,
246+
"type" | "name" | "typeArguments" | "package" | "qualifiedName"
247+
> {
245248
id?: number;
246-
qualifiedName?: string;
247249
}
248250

249251
export interface ReflectionType extends Type, S<M.ReflectionType, "type"> {

src/lib/types/ts-internal/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ declare module "typescript" {
1212
interface Symbol {
1313
// https://github.com/microsoft/TypeScript/blob/v4.1.5/src/compiler/types.ts#L4734-L4737
1414
checkFlags?: CheckFlags;
15+
16+
// https://github.com/microsoft/TypeScript/blob/v4.7.4/src/compiler/types.ts#L4941
17+
// https://github.com/microsoft/TypeScript/issues/38344
18+
parent?: ts.Symbol;
1519
}
1620

1721
interface TypeChecker {

src/lib/utils/tsutils.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
1-
import type * as ts from "typescript";
1+
import * as ts from "typescript";
22

3-
export function getQualifiedName(checker: ts.TypeChecker, symbol: ts.Symbol) {
4-
const qualifiedName = checker.getFullyQualifiedName(symbol);
5-
// I think this is less bad than depending on symbol.parent...
6-
// https://github.com/microsoft/TypeScript/issues/38344
7-
// It will break if someone names a directory with a quote in it, but so will lots
8-
// of other things including other parts of TypeDoc. Until it *actually* breaks someone...
9-
if (qualifiedName.startsWith('"') && qualifiedName.includes('".')) {
10-
return qualifiedName.substring(qualifiedName.indexOf('".', 1) + 2);
11-
} else {
12-
return qualifiedName;
3+
export function getQualifiedName(symbol: ts.Symbol, defaultName: string) {
4+
// Two implementation options for this one:
5+
// 1. Use the internal symbol.parent, to walk up until we hit a source file symbol (if in a module)
6+
// or undefined (if in a global file)
7+
// 2. Use checker.getFullyQualifiedName and parse out the name from the returned string.
8+
// The symbol.parent method is easier to check for now.
9+
let sym: ts.Symbol | undefined = symbol;
10+
const parts: string[] = [];
11+
while (sym && !sym.declarations?.some(ts.isSourceFile)) {
12+
parts.unshift(sym.name);
13+
sym = sym.parent;
1314
}
15+
16+
return parts.join(".") || defaultName;
1417
}

src/test/converter/alias/specs.json

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@
3838
"checkType": {
3939
"type": "reference",
4040
"id": 9,
41-
"name": "T"
41+
"name": "T",
42+
"qualifiedName": "T",
43+
"package": "typedoc"
4244
},
4345
"extendsType": {
4446
"type": "intrinsic",
@@ -88,7 +90,9 @@
8890
"checkType": {
8991
"type": "reference",
9092
"id": 11,
91-
"name": "T"
93+
"name": "T",
94+
"qualifiedName": "T",
95+
"package": "typedoc"
9296
},
9397
"extendsType": {
9498
"type": "reference",
@@ -104,12 +108,16 @@
104108
},
105109
"trueType": {
106110
"type": "reference",
107-
"name": "U"
111+
"name": "U",
112+
"qualifiedName": "U",
113+
"package": "typedoc"
108114
},
109115
"falseType": {
110116
"type": "reference",
111117
"id": 11,
112-
"name": "T"
118+
"name": "T",
119+
"qualifiedName": "T",
120+
"package": "typedoc"
113121
}
114122
}
115123
},
@@ -172,7 +180,9 @@
172180
"type": {
173181
"type": "reference",
174182
"id": 6,
175-
"name": "T"
183+
"name": "T",
184+
"qualifiedName": "T",
185+
"package": "typedoc"
176186
}
177187
},
178188
{
@@ -183,7 +193,9 @@
183193
"type": {
184194
"type": "reference",
185195
"id": 6,
186-
"name": "T"
196+
"name": "T",
197+
"qualifiedName": "T",
198+
"package": "typedoc"
187199
}
188200
}
189201
],

0 commit comments

Comments
 (0)