@openrewrite/rewrite 8.92.16 → 8.92.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/java/formatting-utils.d.ts +2 -1
- package/dist/java/formatting-utils.d.ts.map +1 -1
- package/dist/java/formatting-utils.js +4 -3
- package/dist/java/formatting-utils.js.map +1 -1
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +2 -0
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/tree.d.ts +8 -0
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +11 -1
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type.d.ts +7 -1
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +16 -1
- package/dist/java/type.js.map +1 -1
- package/dist/javascript/add-import.d.ts +27 -21
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +303 -143
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/amd.d.ts.map +1 -1
- package/dist/javascript/amd.js +25 -10
- package/dist/javascript/amd.js.map +1 -1
- package/dist/javascript/autodetect.js +36 -45
- package/dist/javascript/autodetect.js.map +1 -1
- package/dist/javascript/binding.d.ts +6 -4
- package/dist/javascript/binding.d.ts.map +1 -1
- package/dist/javascript/binding.js +54 -18
- package/dist/javascript/binding.js.map +1 -1
- package/dist/javascript/comparator.d.ts +22 -5
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +120 -38
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format/format.d.ts +2 -0
- package/dist/javascript/format/format.d.ts.map +1 -1
- package/dist/javascript/format/format.js +11 -1
- package/dist/javascript/format/format.js.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.d.ts.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.js +31 -4
- package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
- package/dist/javascript/index.d.ts +4 -2
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +10 -1
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/markers.d.ts +12 -0
- package/dist/javascript/markers.d.ts.map +1 -1
- package/dist/javascript/markers.js +81 -2
- package/dist/javascript/markers.js.map +1 -1
- package/dist/javascript/method-matcher.d.ts +13 -2
- package/dist/javascript/method-matcher.d.ts.map +1 -1
- package/dist/javascript/method-matcher.js +32 -19
- package/dist/javascript/method-matcher.js.map +1 -1
- package/dist/javascript/node-resolution-result.js +3 -2
- package/dist/javascript/node-resolution-result.js.map +1 -1
- package/dist/javascript/package-name.d.ts +6 -0
- package/dist/javascript/package-name.d.ts.map +1 -0
- package/dist/javascript/package-name.js +47 -0
- package/dist/javascript/package-name.js.map +1 -0
- package/dist/javascript/parser.d.ts +9 -0
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +185 -88
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/preconditions.d.ts +4 -1
- package/dist/javascript/preconditions.d.ts.map +1 -1
- package/dist/javascript/preconditions.js +7 -4
- package/dist/javascript/preconditions.js.map +1 -1
- package/dist/javascript/print.d.ts +10 -1
- package/dist/javascript/print.d.ts.map +1 -1
- package/dist/javascript/print.js +120 -77
- package/dist/javascript/print.js.map +1 -1
- package/dist/javascript/recipes/change-method-target-to-import.d.ts +29 -0
- package/dist/javascript/recipes/change-method-target-to-import.d.ts.map +1 -0
- package/dist/javascript/recipes/change-method-target-to-import.js +164 -0
- package/dist/javascript/recipes/change-method-target-to-import.js.map +1 -0
- package/dist/javascript/recipes/index.d.ts +1 -0
- package/dist/javascript/recipes/index.d.ts.map +1 -1
- package/dist/javascript/recipes/index.js +1 -0
- package/dist/javascript/recipes/index.js.map +1 -1
- package/dist/javascript/scope.d.ts +60 -16
- package/dist/javascript/scope.d.ts.map +1 -1
- package/dist/javascript/scope.js +596 -131
- package/dist/javascript/scope.js.map +1 -1
- package/dist/javascript/search/uses-method.d.ts +1 -1
- package/dist/javascript/search/uses-method.d.ts.map +1 -1
- package/dist/javascript/search/uses-method.js +2 -2
- package/dist/javascript/search/uses-method.js.map +1 -1
- package/dist/javascript/search/uses-type.d.ts +14 -1
- package/dist/javascript/search/uses-type.d.ts.map +1 -1
- package/dist/javascript/search/uses-type.js +24 -9
- package/dist/javascript/search/uses-type.js.map +1 -1
- package/dist/javascript/templating/bindings.d.ts +1 -1
- package/dist/javascript/templating/bindings.d.ts.map +1 -1
- package/dist/javascript/templating/bindings.js +10 -50
- package/dist/javascript/templating/bindings.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +8 -11
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +12 -110
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +34 -24
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +208 -115
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +11 -5
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +86 -20
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts +34 -3
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +126 -60
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/precedence.d.ts +3 -1
- package/dist/javascript/templating/precedence.d.ts.map +1 -1
- package/dist/javascript/templating/precedence.js +28 -8
- package/dist/javascript/templating/precedence.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +7 -6
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +31 -28
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +6 -3
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +10 -1
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +22 -3
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/tree-debug.d.ts +2 -0
- package/dist/javascript/tree-debug.d.ts.map +1 -1
- package/dist/javascript/tree-debug.js +6 -0
- package/dist/javascript/tree-debug.js.map +1 -1
- package/dist/javascript/tsconfig.d.ts.map +1 -1
- package/dist/javascript/tsconfig.js +7 -17
- package/dist/javascript/tsconfig.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +35 -16
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +271 -252
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/type-report-cli.d.ts +7 -0
- package/dist/javascript/type-report-cli.d.ts.map +1 -0
- package/dist/javascript/type-report-cli.js +129 -0
- package/dist/javascript/type-report-cli.js.map +1 -0
- package/dist/javascript/type-report.d.ts +61 -0
- package/dist/javascript/type-report.d.ts.map +1 -0
- package/dist/javascript/type-report.js +488 -0
- package/dist/javascript/type-report.js.map +1 -0
- package/dist/markers.d.ts +4 -0
- package/dist/markers.d.ts.map +1 -1
- package/dist/markers.js +8 -0
- package/dist/markers.js.map +1 -1
- package/dist/print.d.ts +6 -1
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +21 -7
- package/dist/print.js.map +1 -1
- package/dist/recipe.d.ts +5 -0
- package/dist/recipe.d.ts.map +1 -1
- package/dist/recipe.js +5 -0
- package/dist/recipe.js.map +1 -1
- package/dist/rewrite-javascript-version.txt +1 -1
- package/dist/rpc/java-rpc-client.d.ts +2 -0
- package/dist/rpc/java-rpc-client.d.ts.map +1 -1
- package/dist/rpc/java-rpc-client.js +1 -0
- package/dist/rpc/java-rpc-client.js.map +1 -1
- package/dist/rpc/queue.d.ts +24 -0
- package/dist/rpc/queue.d.ts.map +1 -1
- package/dist/rpc/queue.js +71 -11
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/request/get-object.d.ts +4 -0
- package/dist/rpc/request/get-object.d.ts.map +1 -1
- package/dist/rpc/request/get-object.js +23 -1
- package/dist/rpc/request/get-object.js.map +1 -1
- package/dist/rpc/request/parse.d.ts.map +1 -1
- package/dist/rpc/request/parse.js +3 -1
- package/dist/rpc/request/parse.js.map +1 -1
- package/dist/rpc/request/prepare-recipe.d.ts +3 -1
- package/dist/rpc/request/prepare-recipe.d.ts.map +1 -1
- package/dist/rpc/request/prepare-recipe.js +13 -6
- package/dist/rpc/request/prepare-recipe.js.map +1 -1
- package/dist/rpc/request/print.d.ts +8 -2
- package/dist/rpc/request/print.d.ts.map +1 -1
- package/dist/rpc/request/print.js +12 -9
- package/dist/rpc/request/print.js.map +1 -1
- package/dist/rpc/rewrite-rpc.d.ts +3 -2
- package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
- package/dist/rpc/rewrite-rpc.js +9 -3
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +19 -5
- package/dist/rpc/server.js.map +1 -1
- package/dist/search/is-source-file.d.ts +1 -0
- package/dist/search/is-source-file.d.ts.map +1 -1
- package/dist/search/is-source-file.js +7 -2
- package/dist/search/is-source-file.js.map +1 -1
- package/dist/test/java-rpc.d.ts.map +1 -1
- package/dist/test/java-rpc.js +3 -0
- package/dist/test/java-rpc.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +5 -0
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +14 -3
- package/dist/test/rewrite-test.js.map +1 -1
- package/package.json +4 -3
- package/src/index.ts +3 -1
- package/src/java/formatting-utils.ts +5 -4
- package/src/java/rpc.ts +2 -0
- package/src/java/tree.ts +15 -0
- package/src/java/type.ts +19 -2
- package/src/javascript/add-import.ts +338 -152
- package/src/javascript/amd.ts +28 -12
- package/src/javascript/autodetect.ts +32 -41
- package/src/javascript/binding.ts +64 -22
- package/src/javascript/comparator.ts +129 -43
- package/src/javascript/format/format.ts +9 -0
- package/src/javascript/format/tabs-and-indents-visitor.ts +30 -3
- package/src/javascript/index.ts +6 -3
- package/src/javascript/markers.ts +77 -5
- package/src/javascript/method-matcher.ts +34 -22
- package/src/javascript/node-resolution-result.ts +3 -2
- package/src/javascript/package-name.ts +45 -0
- package/src/javascript/parser.ts +200 -91
- package/src/javascript/preconditions.ts +7 -4
- package/src/javascript/print.ts +123 -76
- package/src/javascript/recipes/change-method-target-to-import.ts +178 -0
- package/src/javascript/recipes/index.ts +1 -0
- package/src/javascript/scope.ts +705 -169
- package/src/javascript/search/uses-method.ts +2 -2
- package/src/javascript/search/uses-type.ts +30 -9
- package/src/javascript/templating/bindings.ts +13 -38
- package/src/javascript/templating/comparator.ts +16 -127
- package/src/javascript/templating/engine.ts +236 -129
- package/src/javascript/templating/pattern.ts +107 -28
- package/src/javascript/templating/placeholder-replacement.ts +154 -62
- package/src/javascript/templating/precedence.ts +33 -11
- package/src/javascript/templating/template.ts +35 -30
- package/src/javascript/templating/types.ts +6 -3
- package/src/javascript/templating/utils.ts +24 -2
- package/src/javascript/tree-debug.ts +9 -0
- package/src/javascript/tsconfig.ts +7 -17
- package/src/javascript/type-mapping.ts +277 -255
- package/src/javascript/type-report-cli.ts +96 -0
- package/src/javascript/type-report.ts +575 -0
- package/src/markers.ts +8 -0
- package/src/print.ts +23 -8
- package/src/recipe.ts +6 -0
- package/src/rpc/java-rpc-client.ts +3 -0
- package/src/rpc/queue.ts +80 -7
- package/src/rpc/request/get-object.ts +24 -0
- package/src/rpc/request/parse.ts +4 -1
- package/src/rpc/request/prepare-recipe.ts +16 -6
- package/src/rpc/request/print.ts +12 -8
- package/src/rpc/rewrite-rpc.ts +16 -5
- package/src/rpc/server.ts +18 -6
- package/src/search/is-source-file.ts +8 -3
- package/src/test/java-rpc.ts +3 -0
- package/src/test/rewrite-test.ts +14 -3
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import ts from "typescript";
|
|
17
17
|
import * as path from "path";
|
|
18
|
+
import {isBuiltin} from "module";
|
|
18
19
|
import {Type} from "../java";
|
|
20
|
+
import {packageNameOf} from "./package-name";
|
|
19
21
|
import FUNCTION_TYPE_NAME = Type.FUNCTION_TYPE_NAME;
|
|
20
22
|
import OBJECT_TYPE_NAME = Type.OBJECT_TYPE_NAME;
|
|
21
23
|
|
|
@@ -37,6 +39,20 @@ function typeSignatureToJSON(this: Type): string {
|
|
|
37
39
|
return Type.signature(this);
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
/**
|
|
43
|
+
* A module specifier with a redundant `node:` scheme removed.
|
|
44
|
+
* `node:url` and `url` import the same built-in, which is named without the scheme.
|
|
45
|
+
* A built-in such as `node:test` has no bare form, and `test` is another package, so it keeps it.
|
|
46
|
+
*/
|
|
47
|
+
function moduleName(specifier: string): string {
|
|
48
|
+
const bare = specifier.substring('node:'.length);
|
|
49
|
+
return specifier.startsWith('node:') && isBuiltin(bare) ? bare : specifier;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isGlobalAugmentation(declaration: ts.Declaration): boolean {
|
|
53
|
+
return ts.isModuleDeclaration(declaration) && (declaration.flags & ts.NodeFlags.GlobalAugmentation) !== 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
40
56
|
export class JavaScriptTypeMapping {
|
|
41
57
|
// Primary cache: Use type signatures (preferring type.id) as cache keys
|
|
42
58
|
// TypeScript assigns stable IDs to all types, so we don't need secondary caches
|
|
@@ -47,6 +63,12 @@ export class JavaScriptTypeMapping {
|
|
|
47
63
|
private readonly stringWrapperType: ts.Type | undefined;
|
|
48
64
|
private readonly numberWrapperType: ts.Type | undefined;
|
|
49
65
|
private readonly booleanWrapperType: ts.Type | undefined;
|
|
66
|
+
private readonly symbolWrapperType: ts.Type | undefined;
|
|
67
|
+
|
|
68
|
+
// The declarations of function types being populated. An instantiation of one of these is
|
|
69
|
+
// unknown, since `type Rec<T> = (x: T) => Rec<T[]>` instantiates without end and each
|
|
70
|
+
// instantiation has a type id of its own that the type cache cannot match.
|
|
71
|
+
private readonly functionTypesInProgress = new Set<ts.Symbol>();
|
|
50
72
|
|
|
51
73
|
|
|
52
74
|
constructor(
|
|
@@ -64,6 +86,7 @@ export class JavaScriptTypeMapping {
|
|
|
64
86
|
const stringSymbol = checker.resolveName("String", undefined, ts.SymbolFlags.Type, false);
|
|
65
87
|
const numberSymbol = checker.resolveName("Number", undefined, ts.SymbolFlags.Type, false);
|
|
66
88
|
const booleanSymbol = checker.resolveName("Boolean", undefined, ts.SymbolFlags.Type, false);
|
|
89
|
+
const symbolSymbol = checker.resolveName("Symbol", undefined, ts.SymbolFlags.Type, false);
|
|
67
90
|
|
|
68
91
|
// Store the TypeScript types; conversion to Type happens on-demand
|
|
69
92
|
if (stringSymbol) {
|
|
@@ -77,6 +100,10 @@ export class JavaScriptTypeMapping {
|
|
|
77
100
|
if (booleanSymbol) {
|
|
78
101
|
this.booleanWrapperType = checker.getDeclaredTypeOfSymbol(booleanSymbol);
|
|
79
102
|
}
|
|
103
|
+
|
|
104
|
+
if (symbolSymbol) {
|
|
105
|
+
this.symbolWrapperType = checker.getDeclaredTypeOfSymbol(symbolSymbol);
|
|
106
|
+
}
|
|
80
107
|
}
|
|
81
108
|
|
|
82
109
|
type(node: ts.Node): Type | undefined {
|
|
@@ -290,10 +317,13 @@ export class JavaScriptTypeMapping {
|
|
|
290
317
|
}
|
|
291
318
|
// Skip instantiated types ONLY if they're not type references
|
|
292
319
|
// Type references like Array<string>, Promise<T> are instantiated but should be mapped
|
|
320
|
+
// Function types, such as a generic method's callback parameter, are mapped too
|
|
293
321
|
// Other instantiated types (like object literals) should return unknown
|
|
294
322
|
if (objectFlags & ts.ObjectFlags.Instantiated) {
|
|
295
323
|
const isTypeReference = objectFlags & ts.ObjectFlags.Reference;
|
|
296
|
-
|
|
324
|
+
const isFunctionType = type.getCallSignatures().length > 0 &&
|
|
325
|
+
!this.functionTypesInProgress.has(type.symbol);
|
|
326
|
+
if (!isTypeReference && !isFunctionType) {
|
|
297
327
|
return Type.unknownType;
|
|
298
328
|
}
|
|
299
329
|
}
|
|
@@ -393,11 +423,7 @@ export class JavaScriptTypeMapping {
|
|
|
393
423
|
if (symbol.flags & ts.SymbolFlags.Function) {
|
|
394
424
|
const callSignatures = type.getCallSignatures();
|
|
395
425
|
if (callSignatures.length > 0) {
|
|
396
|
-
|
|
397
|
-
const functionType = this.createEmptyFunctionType();
|
|
398
|
-
this.typeCache.set(signature, functionType);
|
|
399
|
-
this.populateFunctionType(functionType, callSignatures[0]);
|
|
400
|
-
return functionType;
|
|
426
|
+
return this.createFunctionType(type, callSignatures[0], signature);
|
|
401
427
|
}
|
|
402
428
|
}
|
|
403
429
|
|
|
@@ -405,11 +431,7 @@ export class JavaScriptTypeMapping {
|
|
|
405
431
|
if (symbol.flags & (ts.SymbolFlags.FunctionScopedVariable | ts.SymbolFlags.BlockScopedVariable)) {
|
|
406
432
|
const callSignatures = type.getCallSignatures();
|
|
407
433
|
if (callSignatures.length > 0) {
|
|
408
|
-
|
|
409
|
-
const functionType = this.createEmptyFunctionType();
|
|
410
|
-
this.typeCache.set(signature, functionType);
|
|
411
|
-
this.populateFunctionType(functionType, callSignatures[0]);
|
|
412
|
-
return functionType;
|
|
434
|
+
return this.createFunctionType(type, callSignatures[0], signature);
|
|
413
435
|
}
|
|
414
436
|
}
|
|
415
437
|
|
|
@@ -465,11 +487,7 @@ export class JavaScriptTypeMapping {
|
|
|
465
487
|
// Check for function types without symbols (anonymous functions, function types)
|
|
466
488
|
const callSignatures = type.getCallSignatures();
|
|
467
489
|
if (callSignatures && callSignatures.length > 0) {
|
|
468
|
-
|
|
469
|
-
const functionType = this.createEmptyFunctionType();
|
|
470
|
-
this.typeCache.set(signature, functionType);
|
|
471
|
-
this.populateFunctionType(functionType, callSignatures[0]);
|
|
472
|
-
return functionType;
|
|
490
|
+
return this.createFunctionType(type, callSignatures[0], signature);
|
|
473
491
|
}
|
|
474
492
|
|
|
475
493
|
// A structural type has no nominal name, so every object literal `{a: 1}` and type
|
|
@@ -604,34 +622,20 @@ export class JavaScriptTypeMapping {
|
|
|
604
622
|
// Check if the variable is imported
|
|
605
623
|
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
606
624
|
// For imported variables, find the module specifier
|
|
607
|
-
const
|
|
608
|
-
if (
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
ownerType = {
|
|
622
|
-
kind: Type.Kind.Class,
|
|
623
|
-
flags: 0,
|
|
624
|
-
classKind: Type.Class.Kind.Interface,
|
|
625
|
-
fullyQualifiedName: moduleSpecifier,
|
|
626
|
-
typeParameters: [],
|
|
627
|
-
annotations: [],
|
|
628
|
-
interfaces: [],
|
|
629
|
-
members: [],
|
|
630
|
-
methods: [],
|
|
631
|
-
toJSON: typeSignatureToJSON
|
|
632
|
-
} as Type.Class;
|
|
633
|
-
}
|
|
634
|
-
}
|
|
625
|
+
const moduleSpecifier = this.importedModule(symbol.declarations?.[0]);
|
|
626
|
+
if (moduleSpecifier) {
|
|
627
|
+
ownerType = {
|
|
628
|
+
kind: Type.Kind.Class,
|
|
629
|
+
flags: 0,
|
|
630
|
+
classKind: Type.Class.Kind.Interface,
|
|
631
|
+
fullyQualifiedName: moduleSpecifier,
|
|
632
|
+
typeParameters: [],
|
|
633
|
+
annotations: [],
|
|
634
|
+
interfaces: [],
|
|
635
|
+
members: [],
|
|
636
|
+
methods: [],
|
|
637
|
+
toJSON: typeSignatureToJSON
|
|
638
|
+
} as Type.Class;
|
|
635
639
|
}
|
|
636
640
|
} else {
|
|
637
641
|
// For non-imported variables, check if they belong to a class/interface/namespace
|
|
@@ -683,6 +687,7 @@ export class JavaScriptTypeMapping {
|
|
|
683
687
|
// Create the Type.Variable
|
|
684
688
|
const variable = {
|
|
685
689
|
kind: Type.Kind.Variable,
|
|
690
|
+
flags: 0,
|
|
686
691
|
name: actualSymbol.getName(),
|
|
687
692
|
owner: ownerType,
|
|
688
693
|
type: mappedType,
|
|
@@ -705,7 +710,7 @@ export class JavaScriptTypeMapping {
|
|
|
705
710
|
return undefined;
|
|
706
711
|
}
|
|
707
712
|
const moduleArg = node.arguments[0];
|
|
708
|
-
return ts.isStringLiteral(moduleArg) ? moduleArg.text : undefined;
|
|
713
|
+
return ts.isStringLiteral(moduleArg) ? moduleName(moduleArg.text) : undefined;
|
|
709
714
|
}
|
|
710
715
|
|
|
711
716
|
/**
|
|
@@ -742,14 +747,10 @@ export class JavaScriptTypeMapping {
|
|
|
742
747
|
if (!declaration) {
|
|
743
748
|
return undefined;
|
|
744
749
|
}
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
importDecl = importDecl.parent;
|
|
748
|
-
}
|
|
749
|
-
if (!importDecl || !ts.isStringLiteral((importDecl as ts.ImportDeclaration).moduleSpecifier)) {
|
|
750
|
+
const module = this.importedModule(declaration);
|
|
751
|
+
if (!module) {
|
|
750
752
|
return undefined;
|
|
751
753
|
}
|
|
752
|
-
const module = ((importDecl as ts.ImportDeclaration).moduleSpecifier as ts.StringLiteral).text;
|
|
753
754
|
if (ts.isNamespaceImport(declaration)) {
|
|
754
755
|
return {module, namespace: true};
|
|
755
756
|
}
|
|
@@ -775,6 +776,41 @@ export class JavaScriptTypeMapping {
|
|
|
775
776
|
return binding && binding.exportName === undefined ? binding.module : undefined;
|
|
776
777
|
}
|
|
777
778
|
|
|
779
|
+
/**
|
|
780
|
+
* The module an import declaration containing `node` imports, or undefined when `node` is not
|
|
781
|
+
* part of one or its specifier is not a string literal.
|
|
782
|
+
*/
|
|
783
|
+
private importedModule(node: ts.Node | undefined): string | undefined {
|
|
784
|
+
while (node && !ts.isImportDeclaration(node)) {
|
|
785
|
+
node = node.parent;
|
|
786
|
+
}
|
|
787
|
+
return node && ts.isStringLiteral(node.moduleSpecifier) ? moduleName(node.moduleSpecifier.text) : undefined;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/**
|
|
791
|
+
* A parsed file's module, named after its path relative to the source root without its extension,
|
|
792
|
+
* as TypeScript names the module of an export. A function declared in the file is declared on it.
|
|
793
|
+
*/
|
|
794
|
+
private sourceModuleType(sourceFile: ts.SourceFile): Type.FullyQualified {
|
|
795
|
+
return this.moduleType(this.sourceModuleName(sourceFile));
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
private sourceModuleName(sourceFile: ts.SourceFile): string {
|
|
799
|
+
const fileName = sourceFile.fileName;
|
|
800
|
+
const relative = this.sourceRoot && path.isAbsolute(fileName) ? path.relative(this.sourceRoot, fileName) : fileName;
|
|
801
|
+
return relative.replace(/\.[^/.]+$/, '');
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* The declaration of a function in a parsed source file. A `.d.ts` file is not parsed, so a
|
|
806
|
+
* function such as `parseInt` has none.
|
|
807
|
+
*/
|
|
808
|
+
private parsedFunctionDeclaration(symbol: ts.Symbol): ts.FunctionDeclaration | undefined {
|
|
809
|
+
const declaration = symbol.valueDeclaration;
|
|
810
|
+
return declaration && ts.isFunctionDeclaration(declaration) && !declaration.getSourceFile().isDeclarationFile ?
|
|
811
|
+
declaration : undefined;
|
|
812
|
+
}
|
|
813
|
+
|
|
778
814
|
private moduleType(module: string): Type.FullyQualified {
|
|
779
815
|
return {
|
|
780
816
|
kind: Type.Kind.Class,
|
|
@@ -804,59 +840,16 @@ export class JavaScriptTypeMapping {
|
|
|
804
840
|
const lastNodeModulesIndex = fileName.lastIndexOf('node_modules/');
|
|
805
841
|
const afterNodeModules = fileName.substring(lastNodeModulesIndex + 'node_modules/'.length);
|
|
806
842
|
|
|
807
|
-
// Split by '/' to get path segments
|
|
808
|
-
const segments = afterNodeModules.split('/');
|
|
809
|
-
if (segments.length === 0) {
|
|
810
|
-
return undefined;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
let moduleName: string;
|
|
814
|
-
|
|
815
|
-
// Handle scoped packages (@scope/package)
|
|
816
|
-
if (segments[0].startsWith('@') && segments.length > 1) {
|
|
817
|
-
moduleName = `${segments[0]}/${segments[1]}`;
|
|
818
|
-
} else {
|
|
819
|
-
moduleName = segments[0];
|
|
820
|
-
}
|
|
821
|
-
|
|
822
843
|
// Skip pnpm's .pnpm directory - it contains versioned package paths
|
|
823
844
|
// In pnpm, the actual package is in: .pnpm/pkg@version/node_modules/pkg
|
|
824
845
|
// So we already handled this by using lastIndexOf above
|
|
825
|
-
if (
|
|
846
|
+
if (afterNodeModules.split('/')[0] === '.pnpm') {
|
|
826
847
|
return undefined;
|
|
827
848
|
}
|
|
828
849
|
|
|
829
|
-
return
|
|
850
|
+
return packageNameOf(afterNodeModules);
|
|
830
851
|
}
|
|
831
852
|
|
|
832
|
-
/**
|
|
833
|
-
* Normalize a node_modules package name to the specifier consumers actually import.
|
|
834
|
-
*
|
|
835
|
-
* DefinitelyTyped packages (`@types/<pkg>`) are never importable under that name — the
|
|
836
|
-
* importable specifier is `<pkg>`, with DefinitelyTyped's `__` scoped-package encoding
|
|
837
|
-
* decoded back to a `@scope/name` form. Using the importable specifier keeps attributed
|
|
838
|
-
* fully qualified names consistent regardless of whether a type is reached through a direct
|
|
839
|
-
* import (which already resolves via the module specifier) or transitively (e.g. a call's
|
|
840
|
-
* return type), which falls back to the declaration file's `node_modules` path.
|
|
841
|
-
*
|
|
842
|
-
* Examples:
|
|
843
|
-
* - `@types/express-serve-static-core` -> `express-serve-static-core`
|
|
844
|
-
* - `@types/node` -> `node`
|
|
845
|
-
* - `@types/testing-library__react` -> `@testing-library/react`
|
|
846
|
-
*/
|
|
847
|
-
private normalizePackageName(packageName: string): string {
|
|
848
|
-
if (packageName.startsWith('@types/')) {
|
|
849
|
-
packageName = packageName.substring('@types/'.length);
|
|
850
|
-
// Decode __ encoding for scoped packages: testing-library__react -> @testing-library/react
|
|
851
|
-
if (packageName.includes('__')) {
|
|
852
|
-
const parts = packageName.split('__');
|
|
853
|
-
if (parts.length === 2) {
|
|
854
|
-
packageName = `@${parts[0]}/${parts[1]}`;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
}
|
|
858
|
-
return packageName;
|
|
859
|
-
}
|
|
860
853
|
|
|
861
854
|
/**
|
|
862
855
|
* Helper to create a Type.Method object from common parameters
|
|
@@ -910,6 +903,43 @@ export class JavaScriptTypeMapping {
|
|
|
910
903
|
return method;
|
|
911
904
|
}
|
|
912
905
|
|
|
906
|
+
// A method is declared by one class, which an object literal that spreads a union, for one, is not.
|
|
907
|
+
private declaringType(type: ts.Type): Type.FullyQualified {
|
|
908
|
+
const mapped = this.getType(type);
|
|
909
|
+
switch (mapped.kind) {
|
|
910
|
+
case Type.Kind.Class:
|
|
911
|
+
case Type.Kind.Annotation:
|
|
912
|
+
case Type.Kind.ShallowClass:
|
|
913
|
+
case Type.Kind.Parameterized:
|
|
914
|
+
return mapped as Type.FullyQualified;
|
|
915
|
+
case Type.Kind.Primitive:
|
|
916
|
+
return this.wrapperType(mapped as Type.Primitive);
|
|
917
|
+
default:
|
|
918
|
+
return Type.unknownType as Type.FullyQualified;
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* The class or interface that declares a member, which is a call's declaring type, as in Java.
|
|
924
|
+
* A union's member has one only when every member type inherits it from the same class.
|
|
925
|
+
*/
|
|
926
|
+
private declaringClassOf(member: ts.Symbol): Type.FullyQualified | undefined {
|
|
927
|
+
let owner: ts.Type | undefined;
|
|
928
|
+
for (const declaration of member.declarations ?? []) {
|
|
929
|
+
const parent = declaration.parent;
|
|
930
|
+
if (!ts.isClassLike(parent) && !ts.isInterfaceDeclaration(parent)) {
|
|
931
|
+
return undefined;
|
|
932
|
+
}
|
|
933
|
+
const type = this.checker.getTypeAtLocation(parent);
|
|
934
|
+
if (owner && owner.symbol !== type.symbol) {
|
|
935
|
+
return undefined;
|
|
936
|
+
}
|
|
937
|
+
owner = type;
|
|
938
|
+
}
|
|
939
|
+
const declaringType = owner && this.declaringType(owner);
|
|
940
|
+
return declaringType === Type.unknownType ? undefined : declaringType;
|
|
941
|
+
}
|
|
942
|
+
|
|
913
943
|
private wrapperType(declaringType: (Type.FullyQualified & Type.Primitive) | Type.FullyQualified) {
|
|
914
944
|
if (declaringType === Type.Primitive.String && this.stringWrapperType) {
|
|
915
945
|
return this.getType(this.stringWrapperType) as Type.FullyQualified;
|
|
@@ -1030,8 +1060,10 @@ export class JavaScriptTypeMapping {
|
|
|
1030
1060
|
const anonymous = !mappedType || mappedType.kind !== Type.Kind.Class ||
|
|
1031
1061
|
(mappedType as Type.Class).fullyQualifiedName.startsWith('{');
|
|
1032
1062
|
|
|
1033
|
-
|
|
1034
|
-
if (
|
|
1063
|
+
const declaringClass = this.declaringClassOf(symbol);
|
|
1064
|
+
if (declaringClass) {
|
|
1065
|
+
declaringType = declaringClass;
|
|
1066
|
+
} else if (receiverBinding?.namespace && anonymous) {
|
|
1035
1067
|
declaringType = this.moduleType(receiverBinding.module);
|
|
1036
1068
|
if (methodName === 'default') {
|
|
1037
1069
|
methodName = '<default>';
|
|
@@ -1104,56 +1136,34 @@ export class JavaScriptTypeMapping {
|
|
|
1104
1136
|
// For namespace imports, use the namespace symbol's `name` as the module specifier (e.g. `React` instead of `react`)
|
|
1105
1137
|
moduleSpecifier = aliasedParentSymbol.name;
|
|
1106
1138
|
} else {
|
|
1107
|
-
|
|
1108
|
-
if (exprSymbol.declarations && exprSymbol.declarations.length > 0) {
|
|
1109
|
-
let importNode: ts.Node = exprSymbol.declarations[0];
|
|
1110
|
-
|
|
1111
|
-
// Traverse up to find the ImportDeclaration
|
|
1112
|
-
while (importNode && !ts.isImportDeclaration(importNode)) {
|
|
1113
|
-
importNode = importNode.parent;
|
|
1114
|
-
}
|
|
1115
|
-
|
|
1116
|
-
if (importNode && ts.isImportDeclaration(importNode)) {
|
|
1117
|
-
const importDeclNode = importNode as ts.ImportDeclaration;
|
|
1118
|
-
if (ts.isStringLiteral(importDeclNode.moduleSpecifier)) {
|
|
1119
|
-
moduleSpecifier = importDeclNode.moduleSpecifier.text;
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1139
|
+
moduleSpecifier = this.importedModule(exprSymbol.declarations?.[0]);
|
|
1123
1140
|
}
|
|
1124
1141
|
}
|
|
1125
1142
|
}
|
|
1126
1143
|
|
|
1127
|
-
|
|
1144
|
+
const declared = this.parsedFunctionDeclaration(aliasedSymbol ?? symbol);
|
|
1145
|
+
if (declared) {
|
|
1146
|
+
declaringType = this.sourceModuleType(declared.getSourceFile());
|
|
1147
|
+
methodName = declared.name ? declared.name.text : "<anonymous>";
|
|
1148
|
+
} else if (moduleSpecifier) {
|
|
1128
1149
|
// This is an imported function - use the module specifier as declaring type
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1150
|
+
declaringType = {
|
|
1151
|
+
kind: Type.Kind.Class,
|
|
1152
|
+
flags: 0, // TODO - determine flags
|
|
1153
|
+
fullyQualifiedName: moduleSpecifier
|
|
1154
|
+
} as Type.FullyQualified;
|
|
1155
|
+
// A default import binds an `ImportClause`; its aliased symbol carries the internal
|
|
1156
|
+
// name of the default export (e.g. `e` for express), so represent it as `<default>`.
|
|
1157
|
+
// Named imports (`ImportSpecifier`) keep the original exported name.
|
|
1158
|
+
const isDefaultImport = exprSymbol?.declarations?.some(ts.isImportClause) ?? false;
|
|
1159
|
+
if (isDefaultImport) {
|
|
1160
|
+
methodName = '<default>';
|
|
1161
|
+
} else if (aliasedSymbol?.declarations?.length) {
|
|
1162
|
+
methodName = aliasedSymbol.name;
|
|
1137
1163
|
} else {
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
flags: 0, // TODO - determine flags
|
|
1142
|
-
fullyQualifiedName: moduleSpecifier
|
|
1143
|
-
} as Type.FullyQualified;
|
|
1144
|
-
// A default import binds an `ImportClause`; its aliased symbol carries the internal
|
|
1145
|
-
// name of the default export (e.g. `e` for express), so represent it as `<default>`.
|
|
1146
|
-
// Named imports (`ImportSpecifier`) keep the original exported name.
|
|
1147
|
-
const isDefaultImport = exprSymbol?.declarations?.some(ts.isImportClause) ?? false;
|
|
1148
|
-
if (isDefaultImport) {
|
|
1149
|
-
methodName = '<default>';
|
|
1150
|
-
} else if (aliasedSymbol?.declarations?.length) {
|
|
1151
|
-
methodName = aliasedSymbol.name;
|
|
1152
|
-
} else {
|
|
1153
|
-
// A package without type declarations resolves the import to the checker's
|
|
1154
|
-
// `unknown` symbol, so the exported name comes from the import itself.
|
|
1155
|
-
methodName = this.importBinding(node.expression)?.exportName ?? aliasedSymbol?.name ?? methodName;
|
|
1156
|
-
}
|
|
1164
|
+
// A package without type declarations resolves the import to the checker's
|
|
1165
|
+
// `unknown` symbol, so the exported name comes from the import itself.
|
|
1166
|
+
methodName = this.importBinding(node.expression)?.exportName ?? aliasedSymbol?.name ?? methodName;
|
|
1157
1167
|
}
|
|
1158
1168
|
} else if (this.requiredModuleOfExpression(node.expression)) {
|
|
1159
1169
|
// `const m = require('m'); m()` calls the module's default export.
|
|
@@ -1184,7 +1194,7 @@ export class JavaScriptTypeMapping {
|
|
|
1184
1194
|
const parent = (symbol as any).parent;
|
|
1185
1195
|
if (parent) {
|
|
1186
1196
|
const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
|
|
1187
|
-
declaringType = this.
|
|
1197
|
+
declaringType = this.declaringType(parentType);
|
|
1188
1198
|
} else {
|
|
1189
1199
|
declaringType = Type.unknownType as Type.FullyQualified;
|
|
1190
1200
|
}
|
|
@@ -1220,7 +1230,7 @@ export class JavaScriptTypeMapping {
|
|
|
1220
1230
|
const parent = node.parent;
|
|
1221
1231
|
if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isObjectLiteralExpression(parent)) {
|
|
1222
1232
|
const parentType = this.checker.getTypeAtLocation(parent);
|
|
1223
|
-
declaringType = this.
|
|
1233
|
+
declaringType = this.declaringType(parentType);
|
|
1224
1234
|
} else {
|
|
1225
1235
|
declaringType = Type.unknownType as Type.FullyQualified;
|
|
1226
1236
|
}
|
|
@@ -1243,7 +1253,7 @@ export class JavaScriptTypeMapping {
|
|
|
1243
1253
|
const parent = node.parent;
|
|
1244
1254
|
if (ts.isClassDeclaration(parent) || ts.isClassExpression(parent)) {
|
|
1245
1255
|
const parentType = this.checker.getTypeAtLocation(parent);
|
|
1246
|
-
declaringType = this.
|
|
1256
|
+
declaringType = this.declaringType(parentType);
|
|
1247
1257
|
} else {
|
|
1248
1258
|
declaringType = Type.unknownType as Type.FullyQualified;
|
|
1249
1259
|
}
|
|
@@ -1256,23 +1266,7 @@ export class JavaScriptTypeMapping {
|
|
|
1256
1266
|
|
|
1257
1267
|
methodName = node.name ? node.name.getText() : "<anonymous>";
|
|
1258
1268
|
|
|
1259
|
-
|
|
1260
|
-
// Use the same relativization as getFullyQualifiedName() so that declarations
|
|
1261
|
-
// and invocations produce matching FQNs.
|
|
1262
|
-
let moduleFqn: string;
|
|
1263
|
-
const fileName = node.getSourceFile().fileName;
|
|
1264
|
-
if (this.sourceRoot && path.isAbsolute(fileName)) {
|
|
1265
|
-
moduleFqn = path.relative(this.sourceRoot, fileName);
|
|
1266
|
-
} else {
|
|
1267
|
-
moduleFqn = fileName;
|
|
1268
|
-
}
|
|
1269
|
-
// Strip file extension to get the module name
|
|
1270
|
-
moduleFqn = moduleFqn.replace(/\.[^/.]+$/, '');
|
|
1271
|
-
declaringType = {
|
|
1272
|
-
kind: Type.Kind.Class,
|
|
1273
|
-
flags: 0,
|
|
1274
|
-
fullyQualifiedName: moduleFqn
|
|
1275
|
-
} as Type.FullyQualified;
|
|
1269
|
+
declaringType = this.sourceModuleType(node.getSourceFile());
|
|
1276
1270
|
|
|
1277
1271
|
// Get type parameters from node
|
|
1278
1272
|
if (node.typeParameters) {
|
|
@@ -1310,35 +1304,7 @@ export class JavaScriptTypeMapping {
|
|
|
1310
1304
|
if (symbol.flags & ts.SymbolFlags.Alias) {
|
|
1311
1305
|
const aliasedSymbol = this.checker.getAliasedSymbol(symbol);
|
|
1312
1306
|
if (aliasedSymbol && aliasedSymbol !== symbol && symbol.declarations && symbol.declarations.length > 0) {
|
|
1313
|
-
|
|
1314
|
-
let importNode: ts.Node | undefined = symbol.declarations[0];
|
|
1315
|
-
|
|
1316
|
-
// Traverse up to find the ImportDeclaration or ImportSpecifier
|
|
1317
|
-
while (importNode && importNode.parent && !ts.isImportDeclaration(importNode) && !ts.isImportSpecifier(importNode)) {
|
|
1318
|
-
importNode = importNode.parent;
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
let moduleSpecifier: string | undefined;
|
|
1322
|
-
|
|
1323
|
-
if (importNode && ts.isImportSpecifier(importNode)) {
|
|
1324
|
-
// Named import like: import { ClipLoader } from 'react-spinners'
|
|
1325
|
-
// ImportSpecifier -> NamedImports -> ImportClause -> ImportDeclaration
|
|
1326
|
-
const namedImports = importNode.parent; // NamedImports
|
|
1327
|
-
if (namedImports && ts.isNamedImports(namedImports)) {
|
|
1328
|
-
const importClause = namedImports.parent; // ImportClause
|
|
1329
|
-
if (importClause && ts.isImportClause(importClause)) {
|
|
1330
|
-
const importDecl = importClause.parent; // ImportDeclaration
|
|
1331
|
-
if (importDecl && ts.isImportDeclaration(importDecl) && ts.isStringLiteral(importDecl.moduleSpecifier)) {
|
|
1332
|
-
moduleSpecifier = importDecl.moduleSpecifier.text;
|
|
1333
|
-
}
|
|
1334
|
-
}
|
|
1335
|
-
}
|
|
1336
|
-
} else if (importNode && ts.isImportDeclaration(importNode)) {
|
|
1337
|
-
// Default or namespace import
|
|
1338
|
-
if (ts.isStringLiteral(importNode.moduleSpecifier)) {
|
|
1339
|
-
moduleSpecifier = importNode.moduleSpecifier.text;
|
|
1340
|
-
}
|
|
1341
|
-
}
|
|
1307
|
+
const moduleSpecifier = this.importedModule(symbol.declarations[0]);
|
|
1342
1308
|
|
|
1343
1309
|
if (moduleSpecifier) {
|
|
1344
1310
|
// Build the fully qualified name from module specifier + symbol name
|
|
@@ -1352,47 +1318,35 @@ export class JavaScriptTypeMapping {
|
|
|
1352
1318
|
// This returns names with quotes that we need to clean up
|
|
1353
1319
|
// e.g., '"React"."Component"' -> 'React.Component'
|
|
1354
1320
|
const tsQualifiedName = this.checker.getFullyQualifiedName(symbol);
|
|
1355
|
-
let cleanedName = tsQualifiedName.replace(/"/g, '');
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
if (
|
|
1321
|
+
let cleanedName = moduleName(tsQualifiedName.replace(/"/g, ''));
|
|
1322
|
+
const outermost = this.outermostDeclaration(symbol);
|
|
1323
|
+
const sourceModule = outermost && this.sourceModuleOf(outermost);
|
|
1324
|
+
|
|
1325
|
+
if (sourceModule) {
|
|
1326
|
+
cleanedName = `${sourceModule}.${cleanedName}`;
|
|
1327
|
+
} else if (cleanedName.includes('node_modules/')) {
|
|
1328
|
+
// Check if this is a file path from node_modules (happens with some packages)
|
|
1329
|
+
// TypeScript sometimes returns full paths instead of module names
|
|
1360
1330
|
// Extract the module name from the path
|
|
1361
1331
|
// Example: /private/var/.../node_modules/react-spinners/src/index.ClipLoader
|
|
1362
1332
|
// Should become: react-spinners.ClipLoader
|
|
1363
1333
|
const nodeModulesIndex = cleanedName.indexOf('node_modules/');
|
|
1364
1334
|
const afterNodeModules = cleanedName.substring(nodeModulesIndex + 'node_modules/'.length);
|
|
1365
1335
|
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
// file's node_modules path) match the names used for directly imported types.
|
|
1381
|
-
packageName = this.normalizePackageName(packageName);
|
|
1382
|
-
|
|
1383
|
-
// The symbol name is appended to the declaration file's path, so it is whatever
|
|
1384
|
-
// follows the last dot of the final path segment. Directory names carry dots of
|
|
1385
|
-
// their own — `.pnpm`, `.yarn`, a hidden parent such as `.claude`, a versioned
|
|
1386
|
-
// pnpm directory — so a search over the whole path would split there instead and
|
|
1387
|
-
// fold most of the path into the name.
|
|
1388
|
-
const lastSegmentIndex = cleanedName.lastIndexOf('/') + 1;
|
|
1389
|
-
const lastDotIndex = cleanedName.lastIndexOf('.');
|
|
1390
|
-
if (lastDotIndex > lastSegmentIndex) {
|
|
1391
|
-
const symbolName = cleanedName.substring(lastDotIndex + 1);
|
|
1392
|
-
cleanedName = `${packageName}.${symbolName}`;
|
|
1393
|
-
} else {
|
|
1394
|
-
cleanedName = packageName;
|
|
1395
|
-
}
|
|
1336
|
+
const packageName = packageNameOf(afterNodeModules);
|
|
1337
|
+
|
|
1338
|
+
// The symbol name is appended to the declaration file's path, so it is whatever
|
|
1339
|
+
// follows the last dot of the final path segment. Directory names carry dots of
|
|
1340
|
+
// their own — `.pnpm`, `.yarn`, a hidden parent such as `.claude`, a versioned
|
|
1341
|
+
// pnpm directory — so a search over the whole path would split there instead and
|
|
1342
|
+
// fold most of the path into the name.
|
|
1343
|
+
const lastSegmentIndex = cleanedName.lastIndexOf('/') + 1;
|
|
1344
|
+
const lastDotIndex = cleanedName.lastIndexOf('.');
|
|
1345
|
+
if (lastDotIndex > lastSegmentIndex) {
|
|
1346
|
+
const symbolName = cleanedName.substring(lastDotIndex + 1);
|
|
1347
|
+
cleanedName = `${packageName}.${symbolName}`;
|
|
1348
|
+
} else {
|
|
1349
|
+
cleanedName = packageName;
|
|
1396
1350
|
}
|
|
1397
1351
|
} else if (path.isAbsolute(cleanedName)) {
|
|
1398
1352
|
// TypeScript returns absolute file paths as module names for project source files
|
|
@@ -1429,7 +1383,7 @@ export class JavaScriptTypeMapping {
|
|
|
1429
1383
|
// Preserved as-is:
|
|
1430
1384
|
// - UMD globals (`export as namespace X`, e.g. React, lodash `_`, jQuery `$`): the
|
|
1431
1385
|
// namespace name is the conventional public identifier;
|
|
1432
|
-
// - `global
|
|
1386
|
+
// - `global.*`, a `declare global` augmentation, whose prefix is dropped below;
|
|
1433
1387
|
// - namespaces that already match the package name, and any non-node_modules type.
|
|
1434
1388
|
const namespaceName = cleanedName.substring(0, cleanedName.indexOf('.'));
|
|
1435
1389
|
if (namespaceName !== 'global') {
|
|
@@ -1444,11 +1398,40 @@ export class JavaScriptTypeMapping {
|
|
|
1444
1398
|
}
|
|
1445
1399
|
}
|
|
1446
1400
|
|
|
1401
|
+
// A `declare global` augmentation is in the same global scope as the built-ins, which have no prefix.
|
|
1402
|
+
if (outermost && isGlobalAugmentation(outermost) && cleanedName.startsWith('global.')) {
|
|
1403
|
+
cleanedName = cleanedName.substring('global.'.length);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1447
1406
|
return cleanedName.endsWith('Constructor') ?
|
|
1448
1407
|
cleanedName.substring(0, cleanedName.length - 'Constructor'.length) :
|
|
1449
1408
|
cleanedName;
|
|
1450
1409
|
}
|
|
1451
1410
|
|
|
1411
|
+
/**
|
|
1412
|
+
* The project module whose top level holds a declaration local to it, such as a class it does not
|
|
1413
|
+
* export. TypeScript names that as bare as a global, so a module's own `class Array` needs the module.
|
|
1414
|
+
*/
|
|
1415
|
+
private sourceModuleOf(declaration: ts.Declaration): string | undefined {
|
|
1416
|
+
if (!declaration.parent || !ts.isSourceFile(declaration.parent) || isGlobalAugmentation(declaration) ||
|
|
1417
|
+
ts.isModuleDeclaration(declaration) && ts.isStringLiteral(declaration.name)) {
|
|
1418
|
+
return undefined;
|
|
1419
|
+
}
|
|
1420
|
+
const sourceFile = declaration.parent;
|
|
1421
|
+
const isModule = ts.isExternalModule(sourceFile) || (sourceFile as any).commonJsModuleIndicator !== undefined;
|
|
1422
|
+
return isModule && !sourceFile.isDeclarationFile && !sourceFile.fileName.includes('node_modules/') ?
|
|
1423
|
+
this.sourceModuleName(sourceFile) : undefined;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/** The declaration of the symbol a qualified name starts from, such as a class's namespace. */
|
|
1427
|
+
private outermostDeclaration(symbol: ts.Symbol): ts.Declaration | undefined {
|
|
1428
|
+
let outermost = symbol;
|
|
1429
|
+
while ((outermost as any).parent) {
|
|
1430
|
+
outermost = (outermost as any).parent;
|
|
1431
|
+
}
|
|
1432
|
+
return outermost.declarations?.[0];
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1452
1435
|
/**
|
|
1453
1436
|
* Whether {@code namespaceName} is exposed as a UMD global from the given declaration file
|
|
1454
1437
|
* (e.g. `export as namespace React`). The namespace name of a UMD global is the conventional
|
|
@@ -1501,12 +1484,42 @@ export class JavaScriptTypeMapping {
|
|
|
1501
1484
|
} as Type.Class;
|
|
1502
1485
|
}
|
|
1503
1486
|
|
|
1487
|
+
private baseClass(classType: Type.Class, baseType: ts.Type): Type.Class | undefined {
|
|
1488
|
+
const mapped = this.getType(baseType);
|
|
1489
|
+
const base = Type.isParameterized(mapped) ? mapped.type : mapped;
|
|
1490
|
+
// TypeScript accepts heritage cycles (`class A implements A`). Whichever edge closes one,
|
|
1491
|
+
// in mapping order, is dropped.
|
|
1492
|
+
return Type.isClass(base) && !this.inheritsFrom(base, classType, new Set()) ? base : undefined;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
private inheritsFrom(type: Type.Class, ancestor: Type.Class, seen: Set<Type.Class>): boolean {
|
|
1496
|
+
if (type === ancestor) {
|
|
1497
|
+
return true;
|
|
1498
|
+
}
|
|
1499
|
+
if (seen.has(type)) {
|
|
1500
|
+
return false;
|
|
1501
|
+
}
|
|
1502
|
+
seen.add(type);
|
|
1503
|
+
return (type.supertype !== undefined && this.inheritsFrom(type.supertype, ancestor, seen)) ||
|
|
1504
|
+
type.interfaces.some(i => this.inheritsFrom(i, ancestor, seen));
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
private addInterface(classType: Type.Class, baseType: ts.Type): void {
|
|
1508
|
+
const iface = this.baseClass(classType, baseType);
|
|
1509
|
+
if (iface && !classType.interfaces.includes(iface)) {
|
|
1510
|
+
classType.interfaces.push(iface);
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1504
1514
|
/**
|
|
1505
1515
|
* Populates the class type with members, methods, heritage, and type parameters
|
|
1506
1516
|
* Since the shell is already in the cache, any recursive references will find it
|
|
1507
1517
|
*/
|
|
1508
1518
|
private populateClassType(classType: Type.Class, type: ts.Type): void {
|
|
1509
1519
|
const symbol = type.getSymbol?.();
|
|
1520
|
+
const classSymbol = symbol && symbol.flags & ts.SymbolFlags.Alias ?
|
|
1521
|
+
this.checker.getAliasedSymbol(symbol) : symbol;
|
|
1522
|
+
const classDeclaration = classSymbol?.declarations?.find(ts.isClassLike);
|
|
1510
1523
|
|
|
1511
1524
|
// Try to get base types using TypeScript's getBaseTypes API
|
|
1512
1525
|
// This works for both local and external types (from node_modules)
|
|
@@ -1524,9 +1537,6 @@ export class JavaScriptTypeMapping {
|
|
|
1524
1537
|
} else if (symbol) {
|
|
1525
1538
|
// For constructor functions or type references, we need to get the actual class type
|
|
1526
1539
|
// Try to get the type of the class itself (not the constructor or instance)
|
|
1527
|
-
const classSymbol = symbol.flags & ts.SymbolFlags.Alias ?
|
|
1528
|
-
this.checker.getAliasedSymbol(symbol) : symbol;
|
|
1529
|
-
|
|
1530
1540
|
if (classSymbol && classSymbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) {
|
|
1531
1541
|
// Get the type of the class declaration itself
|
|
1532
1542
|
const declaredType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
|
|
@@ -1552,32 +1562,27 @@ export class JavaScriptTypeMapping {
|
|
|
1552
1562
|
}
|
|
1553
1563
|
|
|
1554
1564
|
if (baseTypes && baseTypes.length > 0) {
|
|
1555
|
-
//
|
|
1556
|
-
|
|
1557
|
-
if (classType.classKind === Type.Class.Kind.Class
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
if (
|
|
1561
|
-
|
|
1562
|
-
} else if (Type.isParameterized(firstBase)) {
|
|
1563
|
-
// For parameterized types, use the base class as the supertype
|
|
1564
|
-
(classType as any).supertype = (firstBase as Type.Parameterized).type;
|
|
1565
|
-
}
|
|
1566
|
-
// Rest are interfaces
|
|
1567
|
-
for (let i = 1; i < baseTypes.length; i++) {
|
|
1568
|
-
const interfaceType = this.getType(baseTypes[i]);
|
|
1569
|
-
if (Type.isClass(interfaceType)) {
|
|
1570
|
-
classType.interfaces.push(interfaceType);
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
} else {
|
|
1574
|
-
// For interfaces, all base types are extended interfaces
|
|
1575
|
-
for (const baseType of baseTypes) {
|
|
1576
|
-
const interfaceType = this.getType(baseType);
|
|
1577
|
-
if (Type.isClass(interfaceType)) {
|
|
1578
|
-
classType.interfaces.push(interfaceType);
|
|
1579
|
-
}
|
|
1565
|
+
// A merged interface's bases follow the class's, so the first is a superclass only beside `extends`
|
|
1566
|
+
let interfaces = baseTypes;
|
|
1567
|
+
if (classType.classKind === Type.Class.Kind.Class && (!classDeclaration ||
|
|
1568
|
+
classDeclaration.heritageClauses?.some(c => c.token === ts.SyntaxKind.ExtendsKeyword))) {
|
|
1569
|
+
const supertype = this.baseClass(classType, baseTypes[0]);
|
|
1570
|
+
if (supertype) {
|
|
1571
|
+
classType.supertype = supertype;
|
|
1580
1572
|
}
|
|
1573
|
+
interfaces = baseTypes.slice(1);
|
|
1574
|
+
}
|
|
1575
|
+
for (const baseType of interfaces) {
|
|
1576
|
+
this.addInterface(classType, baseType);
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
// `getBaseTypes` omits a class's `implements` clause
|
|
1582
|
+
for (const clause of classDeclaration?.heritageClauses ?? []) {
|
|
1583
|
+
if (clause.token === ts.SyntaxKind.ImplementsKeyword) {
|
|
1584
|
+
for (const implemented of clause.types) {
|
|
1585
|
+
this.addInterface(classType, this.checker.getTypeAtLocation(implemented));
|
|
1581
1586
|
}
|
|
1582
1587
|
}
|
|
1583
1588
|
}
|
|
@@ -1616,6 +1621,7 @@ export class JavaScriptTypeMapping {
|
|
|
1616
1621
|
const propType = this.checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
1617
1622
|
const variable: Type.Variable = {
|
|
1618
1623
|
kind: Type.Kind.Variable,
|
|
1624
|
+
flags: 0,
|
|
1619
1625
|
name: prop.getName(),
|
|
1620
1626
|
owner: classType, // Cyclic reference to the containing class (already in cache)
|
|
1621
1627
|
type: this.getType(propType), // This will find classType in cache if it's recursive
|
|
@@ -1661,6 +1667,9 @@ export class JavaScriptTypeMapping {
|
|
|
1661
1667
|
return Type.Primitive.String;
|
|
1662
1668
|
} else if (type.flags & (ts.TypeFlags.Boolean | ts.TypeFlags.BooleanLiteral | ts.TypeFlags.BooleanLike)) {
|
|
1663
1669
|
return Type.Primitive.Boolean;
|
|
1670
|
+
} else if (type.flags & ts.TypeFlags.ESSymbolLike && this.symbolWrapperType) {
|
|
1671
|
+
// Java has no primitive for a symbol, so it is typed as the class it boxes to.
|
|
1672
|
+
return this.getType(this.symbolWrapperType);
|
|
1664
1673
|
}
|
|
1665
1674
|
|
|
1666
1675
|
// Check for type aliases that may resolve to primitives
|
|
@@ -1782,6 +1791,19 @@ export class JavaScriptTypeMapping {
|
|
|
1782
1791
|
} as Type.Class;
|
|
1783
1792
|
}
|
|
1784
1793
|
|
|
1794
|
+
private createFunctionType(type: ts.Type, signature: ts.Signature, cacheKey: string | number): Type.Class {
|
|
1795
|
+
// Shell-cache: Create stub, cache it, then populate (prevents cycles)
|
|
1796
|
+
const functionType = this.createEmptyFunctionType();
|
|
1797
|
+
this.typeCache.set(cacheKey, functionType);
|
|
1798
|
+
this.functionTypesInProgress.add(type.symbol);
|
|
1799
|
+
try {
|
|
1800
|
+
this.populateFunctionType(functionType, signature);
|
|
1801
|
+
} finally {
|
|
1802
|
+
this.functionTypesInProgress.delete(type.symbol);
|
|
1803
|
+
}
|
|
1804
|
+
return functionType;
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1785
1807
|
/**
|
|
1786
1808
|
* Create an empty function type shell with FQN 𝑓.
|
|
1787
1809
|
* The shell will be populated later to handle circular references.
|