@openrewrite/rewrite 8.62.0 → 8.62.2
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/dist/java/rpc.d.ts +3 -3
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +6 -6
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/tree.d.ts +31 -33
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +125 -7
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type.d.ts +70 -57
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +454 -25
- package/dist/java/type.js.map +1 -1
- package/dist/java/visitor.d.ts +4 -4
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +34 -28
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/assertions.d.ts +3 -0
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +78 -1
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/format.js +4 -4
- package/dist/javascript/format.js.map +1 -1
- package/dist/javascript/parser.d.ts +1 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +38 -11
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/rpc.js +1 -4
- package/dist/javascript/rpc.js.map +1 -1
- package/dist/javascript/tree.d.ts +33 -35
- package/dist/javascript/tree.d.ts.map +1 -1
- package/dist/javascript/tree.js +70 -17
- package/dist/javascript/tree.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +44 -8
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +571 -101
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/javascript/visitor.d.ts +2 -2
- package/dist/javascript/visitor.d.ts.map +1 -1
- package/dist/javascript/visitor.js.map +1 -1
- package/dist/json/tree.d.ts +0 -2
- package/dist/json/tree.d.ts.map +1 -1
- package/dist/json/tree.js +12 -2
- package/dist/json/tree.js.map +1 -1
- package/dist/reference.js +1 -1
- package/dist/reference.js.map +1 -1
- package/dist/rpc/chrome-profiler.d.ts +25 -0
- package/dist/rpc/chrome-profiler.d.ts.map +1 -0
- package/dist/rpc/chrome-profiler.js +405 -0
- package/dist/rpc/chrome-profiler.js.map +1 -0
- package/dist/rpc/queue.d.ts +0 -3
- package/dist/rpc/queue.d.ts.map +1 -1
- package/dist/rpc/queue.js +6 -6
- package/dist/rpc/queue.js.map +1 -1
- package/dist/rpc/request/parse.d.ts.map +1 -1
- package/dist/rpc/request/parse.js.map +1 -1
- package/dist/rpc/rewrite-rpc.d.ts +1 -0
- package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
- package/dist/rpc/rewrite-rpc.js +1 -1
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/rpc/server.d.ts.map +1 -1
- package/dist/rpc/server.js +26 -1
- package/dist/rpc/server.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +1 -1
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +18 -2
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/text/tree.d.ts +0 -2
- package/dist/text/tree.d.ts.map +1 -1
- package/dist/text/tree.js +4 -17
- package/dist/text/tree.js.map +1 -1
- package/dist/tree.d.ts +1 -0
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +14 -0
- package/dist/tree.js.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +3 -3
- package/src/java/rpc.ts +18 -15
- package/src/java/tree.ts +68 -38
- package/src/java/type.ts +475 -74
- package/src/java/visitor.ts +45 -39
- package/src/javascript/assertions.ts +52 -1
- package/src/javascript/format.ts +4 -4
- package/src/javascript/parser.ts +51 -18
- package/src/javascript/rpc.ts +8 -10
- package/src/javascript/tree.ts +34 -35
- package/src/javascript/type-mapping.ts +582 -47
- package/src/javascript/visitor.ts +5 -5
- package/src/json/tree.ts +1 -2
- package/src/reference.ts +1 -1
- package/src/rpc/chrome-profiler.ts +373 -0
- package/src/rpc/queue.ts +8 -12
- package/src/rpc/request/parse.ts +1 -1
- package/src/rpc/rewrite-rpc.ts +3 -2
- package/src/rpc/server.ts +30 -1
- package/src/test/rewrite-test.ts +19 -3
- package/src/text/tree.ts +0 -1
- package/src/tree.ts +15 -0
|
@@ -13,17 +13,30 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
import ts from "typescript";
|
|
17
|
+
import {Type} from "../java";
|
|
18
|
+
import {immerable} from "immer";
|
|
19
|
+
|
|
20
|
+
// Helper class to create Type objects that immer won't traverse
|
|
21
|
+
class NonDraftableType {
|
|
22
|
+
[immerable] = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const builtInTypes = new Set([
|
|
26
|
+
'Array', 'Object', 'Function', 'String', 'Number', 'Boolean',
|
|
27
|
+
'Date', 'RegExp', 'Error', 'Promise', 'Map', 'Set', 'WeakMap',
|
|
28
|
+
'WeakSet', 'Symbol', 'BigInt', 'HTMLElement', 'Document',
|
|
29
|
+
'Window', 'Console', 'JSON', 'Math', 'Reflect', 'Proxy'
|
|
30
|
+
]);
|
|
21
31
|
|
|
22
32
|
export class JavaScriptTypeMapping {
|
|
23
|
-
private readonly typeCache: Map<number,
|
|
33
|
+
private readonly typeCache: Map<string | number, Type> = new Map();
|
|
24
34
|
private readonly regExpSymbol: ts.Symbol | undefined;
|
|
25
35
|
|
|
26
|
-
constructor(
|
|
36
|
+
constructor(
|
|
37
|
+
private readonly checker: ts.TypeChecker,
|
|
38
|
+
private readonly projectRoot: string = process.cwd()
|
|
39
|
+
) {
|
|
27
40
|
this.regExpSymbol = checker.resolveName(
|
|
28
41
|
"RegExp",
|
|
29
42
|
undefined,
|
|
@@ -32,7 +45,7 @@ export class JavaScriptTypeMapping {
|
|
|
32
45
|
);
|
|
33
46
|
}
|
|
34
47
|
|
|
35
|
-
type(node: ts.Node):
|
|
48
|
+
type(node: ts.Node): Type | undefined {
|
|
36
49
|
let type: ts.Type | undefined;
|
|
37
50
|
if (ts.isExpression(node)) {
|
|
38
51
|
type = this.checker.getTypeAtLocation(node);
|
|
@@ -42,84 +55,610 @@ export class JavaScriptTypeMapping {
|
|
|
42
55
|
return type && this.getType(type);
|
|
43
56
|
}
|
|
44
57
|
|
|
45
|
-
private getType(type: ts.Type) {
|
|
58
|
+
private getType(type: ts.Type): Type {
|
|
46
59
|
const signature = this.getSignature(type);
|
|
47
60
|
const existing = this.typeCache.get(signature);
|
|
48
61
|
if (existing) {
|
|
49
62
|
return existing;
|
|
50
63
|
}
|
|
51
|
-
|
|
64
|
+
|
|
65
|
+
// Check if this is an array type BEFORE checking for classes
|
|
66
|
+
// TypeScript represents Array<T> as a reference to the Array interface
|
|
67
|
+
if (this.checker.isArrayType(type)) {
|
|
68
|
+
const arrayType = this.createArrayType(type as ts.TypeReference);
|
|
69
|
+
this.typeCache.set(signature, arrayType);
|
|
70
|
+
return arrayType;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Check for class/interface/enum types (but not arrays)
|
|
74
|
+
const symbol = type.getSymbol?.();
|
|
75
|
+
if (symbol) {
|
|
76
|
+
if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface | ts.SymbolFlags.Enum | ts.SymbolFlags.TypeAlias)) {
|
|
77
|
+
// Create and cache shell first to handle circular references
|
|
78
|
+
const classType = this.createEmptyClassType(type);
|
|
79
|
+
this.typeCache.set(signature, classType);
|
|
80
|
+
this.populateClassType(classType, type);
|
|
81
|
+
return classType;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// For anonymous object types that could have circular references
|
|
86
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
87
|
+
const objectFlags = (type as ts.ObjectType).objectFlags;
|
|
88
|
+
if (objectFlags & ts.ObjectFlags.Anonymous) {
|
|
89
|
+
// Anonymous object type - create and cache shell, then populate
|
|
90
|
+
const classType = this.createEmptyClassType(type);
|
|
91
|
+
this.typeCache.set(signature, classType);
|
|
92
|
+
this.populateClassType(classType, type);
|
|
93
|
+
return classType;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// For non-object types, we can create them directly without recursion concerns
|
|
98
|
+
const result = this.createPrimitiveOrUnknownType(type);
|
|
52
99
|
this.typeCache.set(signature, result);
|
|
53
100
|
return result;
|
|
54
101
|
}
|
|
55
102
|
|
|
56
|
-
private getSignature(type: ts.Type): number {
|
|
57
|
-
//
|
|
58
|
-
if ("id" in type
|
|
103
|
+
private getSignature(type: ts.Type): string | number {
|
|
104
|
+
// Try to use TypeScript's internal id if available
|
|
105
|
+
if ("id" in type && type.id !== undefined) {
|
|
59
106
|
return type.id as number;
|
|
60
|
-
} else {
|
|
61
|
-
throw new Error("no id property in type: " + JSON.stringify(type));
|
|
62
107
|
}
|
|
108
|
+
|
|
109
|
+
// Fallback: Generate a string signature based on type characteristics
|
|
110
|
+
const typeString = this.checker.typeToString(type);
|
|
111
|
+
const symbol = type.getSymbol?.();
|
|
112
|
+
|
|
113
|
+
if (symbol) {
|
|
114
|
+
const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
|
|
115
|
+
if (declaration) {
|
|
116
|
+
const sourceFile = declaration.getSourceFile();
|
|
117
|
+
const fileName = sourceFile.fileName;
|
|
118
|
+
const pos = declaration.pos;
|
|
119
|
+
// Create unique signature from file + position + type string
|
|
120
|
+
// This ensures types from different modules are distinguished
|
|
121
|
+
return `${fileName}:${pos}:${typeString}`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Last resort: use type string with a prefix to distinguish from numeric IDs
|
|
126
|
+
// This might happen for synthetic types or types without declarations
|
|
127
|
+
return `synthetic:${typeString}`;
|
|
63
128
|
}
|
|
64
129
|
|
|
65
|
-
primitiveType(node: ts.Node):
|
|
130
|
+
primitiveType(node: ts.Node): Type.Primitive {
|
|
66
131
|
const type = this.type(node);
|
|
67
|
-
|
|
132
|
+
if (Type.isClass(type) && type.fullyQualifiedName === 'lib.RegExp') {
|
|
133
|
+
return Type.Primitive.String;
|
|
134
|
+
}
|
|
135
|
+
return Type.isPrimitive(type) ? type : Type.Primitive.None;
|
|
68
136
|
}
|
|
69
137
|
|
|
70
|
-
variableType(node: ts.NamedDeclaration):
|
|
138
|
+
variableType(node: ts.NamedDeclaration): Type.Variable | undefined {
|
|
71
139
|
if (ts.isVariableDeclaration(node)) {
|
|
72
140
|
const symbol = this.checker.getSymbolAtLocation(node.name);
|
|
73
141
|
if (symbol) {
|
|
74
|
-
|
|
142
|
+
// TODO: Implement in Phase 6
|
|
143
|
+
// const type = this.checker.getTypeOfSymbolAtLocation(symbol, node);
|
|
144
|
+
// return JavaType.Variable with proper mapping
|
|
75
145
|
}
|
|
76
146
|
}
|
|
77
147
|
return undefined;
|
|
78
148
|
}
|
|
79
149
|
|
|
80
|
-
methodType(node: ts.Node):
|
|
81
|
-
|
|
150
|
+
methodType(node: ts.Node): Type.Method | undefined {
|
|
151
|
+
let signature: ts.Signature | undefined;
|
|
152
|
+
let methodName: string;
|
|
153
|
+
let declaringType: Type.FullyQualified;
|
|
154
|
+
let declaredFormalTypeNames: string[] = [];
|
|
155
|
+
|
|
156
|
+
// Handle different kinds of nodes that represent methods or method invocations
|
|
157
|
+
if (ts.isCallExpression(node)) {
|
|
158
|
+
// For method invocations (e.g., _.map(...))
|
|
159
|
+
signature = this.checker.getResolvedSignature(node);
|
|
160
|
+
if (!signature) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const symbol = this.checker.getSymbolAtLocation(node.expression);
|
|
165
|
+
if (!symbol) {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Get the method name
|
|
170
|
+
if (ts.isPropertyAccessExpression(node.expression)) {
|
|
171
|
+
methodName = node.expression.name.getText();
|
|
172
|
+
const exprType = this.checker.getTypeAtLocation(node.expression.expression);
|
|
173
|
+
declaringType = this.getType(exprType) as Type.FullyQualified;
|
|
174
|
+
} else if (ts.isIdentifier(node.expression)) {
|
|
175
|
+
methodName = node.expression.getText();
|
|
176
|
+
// For standalone functions, we need to determine the appropriate declaring type
|
|
177
|
+
const exprType = this.checker.getTypeAtLocation(node.expression);
|
|
178
|
+
const funcType = this.getType(exprType);
|
|
179
|
+
|
|
180
|
+
if (funcType && funcType.kind === Type.Kind.Class) {
|
|
181
|
+
const fqn = (funcType as Type.Class).fullyQualifiedName;
|
|
182
|
+
const lastDot = fqn.lastIndexOf('.');
|
|
183
|
+
|
|
184
|
+
if (lastDot > 0) {
|
|
185
|
+
// For functions from modules, use the module part as declaring type
|
|
186
|
+
// Examples:
|
|
187
|
+
// - "node.assert" -> declaring type: "node"
|
|
188
|
+
// - "@types/lodash.map" -> declaring type: "@types/lodash"
|
|
189
|
+
// - "@types/express.express" -> declaring type: "@types/express"
|
|
190
|
+
declaringType = {
|
|
191
|
+
kind: Type.Kind.Class,
|
|
192
|
+
fullyQualifiedName: fqn.substring(0, lastDot)
|
|
193
|
+
} as Type.FullyQualified;
|
|
194
|
+
} else {
|
|
195
|
+
// No dots in the name - the type IS the module itself
|
|
196
|
+
// This handles single-name modules like "axios", "lodash" etc.
|
|
197
|
+
declaringType = funcType as Type.FullyQualified;
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
// Try to use the symbol's parent or module
|
|
201
|
+
const parent = (symbol as any).parent;
|
|
202
|
+
if (parent) {
|
|
203
|
+
const parentType = this.checker.getDeclaredTypeOfSymbol(parent);
|
|
204
|
+
declaringType = this.getType(parentType) as Type.FullyQualified;
|
|
205
|
+
} else {
|
|
206
|
+
declaringType = Type.unknownType as Type.FullyQualified;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} else {
|
|
210
|
+
methodName = symbol.getName();
|
|
211
|
+
declaringType = Type.unknownType as Type.FullyQualified;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Get type parameters from signature
|
|
215
|
+
const typeParameters = signature.getTypeParameters();
|
|
216
|
+
if (typeParameters) {
|
|
217
|
+
for (const tp of typeParameters) {
|
|
218
|
+
declaredFormalTypeNames.push(tp.symbol.getName());
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
} else if (ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) {
|
|
222
|
+
// For method declarations
|
|
223
|
+
const symbol = this.checker.getSymbolAtLocation(node.name!);
|
|
224
|
+
if (!symbol) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
signature = this.checker.getSignatureFromDeclaration(node);
|
|
229
|
+
if (!signature) {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
methodName = symbol.getName();
|
|
234
|
+
|
|
235
|
+
// Get the declaring type (the class/interface that contains this method)
|
|
236
|
+
const parent = node.parent;
|
|
237
|
+
if (ts.isClassDeclaration(parent) || ts.isInterfaceDeclaration(parent) || ts.isObjectLiteralExpression(parent)) {
|
|
238
|
+
const parentType = this.checker.getTypeAtLocation(parent);
|
|
239
|
+
declaringType = this.getType(parentType) as Type.FullyQualified;
|
|
240
|
+
} else {
|
|
241
|
+
declaringType = Type.unknownType as Type.FullyQualified;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Get type parameters from node
|
|
245
|
+
if (node.typeParameters) {
|
|
246
|
+
for (const tp of node.typeParameters) {
|
|
247
|
+
declaredFormalTypeNames.push(tp.name.getText());
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} else if (ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) {
|
|
251
|
+
// For function declarations/expressions
|
|
252
|
+
signature = this.checker.getSignatureFromDeclaration(node);
|
|
253
|
+
if (!signature) {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
methodName = node.name ? node.name.getText() : "<anonymous>";
|
|
258
|
+
declaringType = Type.unknownType as Type.FullyQualified;
|
|
259
|
+
|
|
260
|
+
// Get type parameters from node
|
|
261
|
+
if (node.typeParameters) {
|
|
262
|
+
for (const tp of node.typeParameters) {
|
|
263
|
+
declaredFormalTypeNames.push(tp.name.getText());
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
// For other node types, return undefined
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Common logic for all method types
|
|
272
|
+
const returnType = signature.getReturnType();
|
|
273
|
+
const parameters = signature.getParameters();
|
|
274
|
+
const parameterTypes: Type[] = [];
|
|
275
|
+
const parameterNames: string[] = [];
|
|
276
|
+
|
|
277
|
+
for (const param of parameters) {
|
|
278
|
+
parameterNames.push(param.getName());
|
|
279
|
+
const paramType = this.checker.getTypeOfSymbolAtLocation(param, node);
|
|
280
|
+
parameterTypes.push(this.getType(paramType));
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Create the Type.Method object
|
|
284
|
+
return Object.assign(new NonDraftableType(), {
|
|
285
|
+
kind: Type.Kind.Method,
|
|
286
|
+
declaringType: declaringType,
|
|
287
|
+
name: methodName,
|
|
288
|
+
returnType: this.getType(returnType),
|
|
289
|
+
parameterNames: parameterNames,
|
|
290
|
+
parameterTypes: parameterTypes,
|
|
291
|
+
thrownExceptions: [], // JavaScript doesn't have checked exceptions
|
|
292
|
+
annotations: [],
|
|
293
|
+
defaultValue: undefined,
|
|
294
|
+
declaredFormalTypeNames: declaredFormalTypeNames,
|
|
295
|
+
toJSON: function () {
|
|
296
|
+
return Type.signature(this);
|
|
297
|
+
}
|
|
298
|
+
}) as Type.Method;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Create a JavaType.Array from a TypeScript array type
|
|
303
|
+
*/
|
|
304
|
+
private createArrayType(type: ts.TypeReference): Type.Array {
|
|
305
|
+
// Get the element type (type argument of Array<T>)
|
|
306
|
+
const typeArgs = this.checker.getTypeArguments(type);
|
|
307
|
+
const elemType = typeArgs.length > 0 ? this.getType(typeArgs[0]) : Type.unknownType;
|
|
308
|
+
|
|
309
|
+
return Object.assign(new NonDraftableType(), {
|
|
310
|
+
kind: Type.Kind.Array,
|
|
311
|
+
elemType: elemType,
|
|
312
|
+
annotations: [],
|
|
313
|
+
toJSON: function () {
|
|
314
|
+
return Type.signature(this);
|
|
315
|
+
}
|
|
316
|
+
}) as Type.Array;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Get the fully qualified name for a TypeScript type.
|
|
321
|
+
* Format: "module-specifier.TypeName" (e.g., "@mui/material.Button", "src/components/Button.Button")
|
|
322
|
+
*/
|
|
323
|
+
private getFullyQualifiedName(type: ts.Type): string {
|
|
324
|
+
const symbol = type.getSymbol?.();
|
|
325
|
+
if (!symbol) {
|
|
326
|
+
return "unknown";
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const typeName = symbol.getName();
|
|
330
|
+
const declaration = symbol.valueDeclaration || symbol.declarations?.[0];
|
|
331
|
+
if (!declaration) {
|
|
332
|
+
// No declaration - might be a built-in or synthetic type
|
|
333
|
+
if (builtInTypes.has(typeName)) {
|
|
334
|
+
return `lib.${typeName}`;
|
|
335
|
+
}
|
|
336
|
+
return typeName;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const sourceFile = declaration.getSourceFile();
|
|
340
|
+
const fileName = sourceFile.fileName;
|
|
341
|
+
|
|
342
|
+
// Check if this is a test file (snowflake ID as filename)
|
|
343
|
+
// Test files are generated with numeric IDs like "672087069480189952.ts"
|
|
344
|
+
if (/^\d+\.(ts|tsx|js|jsx)$/.test(fileName)) {
|
|
345
|
+
// For test files, just return the type name without module prefix
|
|
346
|
+
return typeName;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Check if this is from TypeScript's lib files (lib.d.ts, lib.dom.d.ts, etc.)
|
|
350
|
+
if (fileName.includes("/typescript/lib/lib.") || fileName.includes("\\typescript\\lib\\lib.")) {
|
|
351
|
+
return `lib.${typeName}`;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Check if this is from an external module (node_modules or .d.ts)
|
|
355
|
+
if (sourceFile.isDeclarationFile || fileName.includes("node_modules")) {
|
|
356
|
+
const packageName = this.extractPackageName(fileName);
|
|
357
|
+
if (packageName) {
|
|
358
|
+
// Special handling for @types/node - these are Node.js built-in modules
|
|
359
|
+
// and should be mapped to "node.*" instead of "@types/node.*"
|
|
360
|
+
if (packageName === "@types/node") {
|
|
361
|
+
// Extract the module name from the file path
|
|
362
|
+
// e.g., /node_modules/@types/node/assert.d.ts -> assert
|
|
363
|
+
// e.g., /node_modules/@types/node/fs/promises.d.ts -> fs/promises
|
|
364
|
+
const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
|
|
365
|
+
if (nodeMatch) {
|
|
366
|
+
const modulePath = nodeMatch[1];
|
|
367
|
+
// For default exports from Node modules, we want the module to be the "class"
|
|
368
|
+
// But we still need to include the type name for proper identification
|
|
369
|
+
if (typeName === "default" || typeName === modulePath) {
|
|
370
|
+
// This is likely the default export, just use the module name
|
|
371
|
+
return `node.${modulePath}`;
|
|
372
|
+
}
|
|
373
|
+
// For named exports, include both module and type name
|
|
374
|
+
if (modulePath.includes('/')) {
|
|
375
|
+
return `node.${modulePath.replace(/\//g, '.')}.${typeName}`;
|
|
376
|
+
}
|
|
377
|
+
return `node.${modulePath}.${typeName}`;
|
|
378
|
+
}
|
|
379
|
+
// Fallback for @types/node types that don't match the pattern
|
|
380
|
+
return `node.${typeName}`;
|
|
381
|
+
}
|
|
382
|
+
return `${packageName}.${typeName}`;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// For local files, use relative path from project root
|
|
387
|
+
const relativePath = this.getRelativeModulePath(fileName);
|
|
388
|
+
return `${relativePath}.${typeName}`;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Extract package name from a node_modules path.
|
|
393
|
+
* Examples:
|
|
394
|
+
* - /path/to/project/node_modules/react/index.d.ts -> "react"
|
|
395
|
+
* - /path/to/project/node_modules/@mui/material/Button/index.d.ts -> "@mui/material"
|
|
396
|
+
*/
|
|
397
|
+
private extractPackageName(fileName: string): string | null {
|
|
398
|
+
const match = fileName.match(/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/);
|
|
399
|
+
return match ? match[1] : null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Get relative module path from project root.
|
|
404
|
+
* Removes file extension and uses forward slashes.
|
|
405
|
+
*/
|
|
406
|
+
private getRelativeModulePath(fileName: string): string {
|
|
407
|
+
// Remove project root and normalize path
|
|
408
|
+
let relativePath = fileName;
|
|
409
|
+
if (fileName.startsWith(this.projectRoot)) {
|
|
410
|
+
relativePath = fileName.slice(this.projectRoot.length);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Remove leading slash and file extension
|
|
414
|
+
relativePath = relativePath.replace(/^\//, '').replace(/\.[^/.]+$/, '');
|
|
415
|
+
|
|
416
|
+
// Convert backslashes to forward slashes (for Windows)
|
|
417
|
+
relativePath = relativePath.replace(/\\/g, '/');
|
|
418
|
+
|
|
419
|
+
return relativePath;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Create an empty JavaType.Class shell from a TypeScript type.
|
|
424
|
+
* The shell will be populated later to handle circular references.
|
|
425
|
+
*/
|
|
426
|
+
private createEmptyClassType(type: ts.Type): Type.Class {
|
|
427
|
+
// Use our custom getFullyQualifiedName method for consistent naming
|
|
428
|
+
let fullyQualifiedName = this.getFullyQualifiedName(type);
|
|
429
|
+
|
|
430
|
+
// If getFullyQualifiedName returned unknown, fall back to TypeScript's method
|
|
431
|
+
if (fullyQualifiedName === "unknown") {
|
|
432
|
+
const symbol = type.symbol;
|
|
433
|
+
fullyQualifiedName = symbol ? this.checker.getFullyQualifiedName(symbol) : `<anonymous>${this.checker.typeToString(type)}`;
|
|
434
|
+
|
|
435
|
+
// Fix FQN for types from @types packages
|
|
436
|
+
// TypeScript returns "_.LoDashStatic" but we want "@types/lodash.LoDashStatic"
|
|
437
|
+
if (symbol && symbol.declarations && symbol.declarations.length > 0) {
|
|
438
|
+
const sourceFile = symbol.declarations[0].getSourceFile();
|
|
439
|
+
const fileName = sourceFile.fileName;
|
|
440
|
+
// Check if this is from @types package
|
|
441
|
+
const typesMatch = fileName.match(/node_modules\/@types\/([^/]+)/);
|
|
442
|
+
if (typesMatch) {
|
|
443
|
+
const packageName = typesMatch[1];
|
|
444
|
+
// Special handling for @types/node - use "node" prefix instead
|
|
445
|
+
if (packageName === "node") {
|
|
446
|
+
// Extract the module name from the file path if possible
|
|
447
|
+
const nodeMatch = fileName.match(/node_modules\/@types\/node\/([^.]+)\.d\.ts/);
|
|
448
|
+
if (nodeMatch) {
|
|
449
|
+
const modulePath = nodeMatch[1];
|
|
450
|
+
// Replace the module specifier with node.module
|
|
451
|
+
fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.${modulePath}.`);
|
|
452
|
+
} else {
|
|
453
|
+
// Fallback: just use "node" prefix
|
|
454
|
+
fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `node.`);
|
|
455
|
+
}
|
|
456
|
+
} else {
|
|
457
|
+
// Replace the module specifier part with @types/package
|
|
458
|
+
fullyQualifiedName = fullyQualifiedName.replace(/^[^.]+\./, `@types/${packageName}.`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Determine the class kind based on symbol flags
|
|
465
|
+
let classKind = Type.Class.Kind.Interface; // Default to interface
|
|
466
|
+
const symbol = type.getSymbol?.();
|
|
467
|
+
if (symbol) {
|
|
468
|
+
if (symbol.flags & ts.SymbolFlags.Class) {
|
|
469
|
+
classKind = Type.Class.Kind.Class;
|
|
470
|
+
} else if (symbol.flags & ts.SymbolFlags.Enum) {
|
|
471
|
+
classKind = Type.Class.Kind.Enum;
|
|
472
|
+
} else if (symbol.flags & ts.SymbolFlags.Interface) {
|
|
473
|
+
classKind = Type.Class.Kind.Interface;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// Create empty class type shell (no members yet to avoid recursion)
|
|
478
|
+
return Object.assign(new NonDraftableType(), {
|
|
479
|
+
kind: Type.Kind.Class,
|
|
480
|
+
classKind: classKind,
|
|
481
|
+
fullyQualifiedName: fullyQualifiedName,
|
|
482
|
+
typeParameters: [],
|
|
483
|
+
annotations: [],
|
|
484
|
+
interfaces: [],
|
|
485
|
+
members: [],
|
|
486
|
+
methods: [],
|
|
487
|
+
toJSON: function () {
|
|
488
|
+
return Type.signature(this);
|
|
489
|
+
}
|
|
490
|
+
}) as Type.Class;
|
|
82
491
|
}
|
|
83
492
|
|
|
84
|
-
|
|
85
|
-
|
|
493
|
+
/**
|
|
494
|
+
* Populates the class type with members, methods, heritage, and type parameters
|
|
495
|
+
* Since the shell is already in the cache, any recursive references will find it
|
|
496
|
+
*/
|
|
497
|
+
private populateClassType(classType: Type.Class, type: ts.Type): void {
|
|
498
|
+
const symbol = type.getSymbol?.();
|
|
499
|
+
|
|
500
|
+
// Try to get base types using TypeScript's getBaseTypes API
|
|
501
|
+
// This works for both local and external types (from node_modules)
|
|
502
|
+
if (type.flags & ts.TypeFlags.Object) {
|
|
503
|
+
let baseTypes: ts.Type[] | undefined;
|
|
504
|
+
|
|
505
|
+
// Check if this is a class or interface type that supports getBaseTypes
|
|
506
|
+
const objectType = type as ts.ObjectType;
|
|
507
|
+
if (objectType.objectFlags & (ts.ObjectFlags.Class | ts.ObjectFlags.Interface)) {
|
|
508
|
+
try {
|
|
509
|
+
baseTypes = (this.checker as any).getBaseTypes?.(type as ts.InterfaceType);
|
|
510
|
+
} catch (e) {
|
|
511
|
+
// getBaseTypes might fail for some types, fall back to declaration-based extraction
|
|
512
|
+
}
|
|
513
|
+
} else if (symbol) {
|
|
514
|
+
// For constructor functions or type references, we need to get the actual class type
|
|
515
|
+
// Try to get the type of the class itself (not the constructor or instance)
|
|
516
|
+
const classSymbol = symbol.flags & ts.SymbolFlags.Alias ?
|
|
517
|
+
this.checker.getAliasedSymbol(symbol) : symbol;
|
|
518
|
+
|
|
519
|
+
if (classSymbol && classSymbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Interface)) {
|
|
520
|
+
// Get the type of the class declaration itself
|
|
521
|
+
const declaredType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
|
|
522
|
+
if (declaredType && declaredType !== type) {
|
|
523
|
+
try {
|
|
524
|
+
baseTypes = (this.checker as any).getBaseTypes?.(declaredType as ts.InterfaceType);
|
|
525
|
+
} catch (e) {
|
|
526
|
+
// getBaseTypes might fail, fall back to declaration-based extraction
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
} else if (classSymbol && classSymbol.valueDeclaration && ts.isClassDeclaration(classSymbol.valueDeclaration)) {
|
|
530
|
+
// Handle the case where the symbol is for a class value (constructor function)
|
|
531
|
+
// Get the instance type of the class
|
|
532
|
+
const instanceType = this.checker.getDeclaredTypeOfSymbol(classSymbol);
|
|
533
|
+
if (instanceType && instanceType !== type) {
|
|
534
|
+
try {
|
|
535
|
+
baseTypes = (this.checker as any).getBaseTypes?.(instanceType as ts.InterfaceType);
|
|
536
|
+
} catch (e) {
|
|
537
|
+
// getBaseTypes might fail, fall back to declaration-based extraction
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (baseTypes && baseTypes.length > 0) {
|
|
544
|
+
// For classes, the first base type is usually the superclass
|
|
545
|
+
// Additional base types are interfaces
|
|
546
|
+
if (classType.classKind === Type.Class.Kind.Class) {
|
|
547
|
+
const firstBase = this.getType(baseTypes[0]);
|
|
548
|
+
if (Type.isClass(firstBase)) {
|
|
549
|
+
(classType as any).supertype = firstBase;
|
|
550
|
+
}
|
|
551
|
+
// Rest are interfaces
|
|
552
|
+
for (let i = 1; i < baseTypes.length; i++) {
|
|
553
|
+
const interfaceType = this.getType(baseTypes[i]);
|
|
554
|
+
if (Type.isClass(interfaceType)) {
|
|
555
|
+
classType.interfaces.push(interfaceType);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
} else {
|
|
559
|
+
// For interfaces, all base types are extended interfaces
|
|
560
|
+
for (const baseType of baseTypes) {
|
|
561
|
+
const interfaceType = this.getType(baseType);
|
|
562
|
+
if (Type.isClass(interfaceType)) {
|
|
563
|
+
classType.interfaces.push(interfaceType);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Extract type parameters from declarations (not provided by getBaseTypes)
|
|
571
|
+
if (symbol?.declarations) {
|
|
572
|
+
for (const declaration of symbol.declarations) {
|
|
573
|
+
if (ts.isClassDeclaration(declaration) || ts.isInterfaceDeclaration(declaration)) {
|
|
574
|
+
// Extract type parameters
|
|
575
|
+
if (declaration.typeParameters) {
|
|
576
|
+
for (const tp of declaration.typeParameters) {
|
|
577
|
+
const tpType = this.checker.getTypeAtLocation(tp);
|
|
578
|
+
classType.typeParameters.push(this.getType(tpType));
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
break; // Only process the first declaration
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Get properties and methods
|
|
587
|
+
const properties = this.checker.getPropertiesOfType(type);
|
|
588
|
+
for (const prop of properties) {
|
|
589
|
+
const declaration = prop.valueDeclaration || prop.declarations?.[0];
|
|
590
|
+
if (!declaration) {
|
|
591
|
+
// Skip properties without declarations (synthetic/built-in properties)
|
|
592
|
+
continue;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (prop.flags & ts.SymbolFlags.Method) {
|
|
596
|
+
// TODO: Create Type.Method when method support is added
|
|
597
|
+
// For now, skip methods
|
|
598
|
+
continue;
|
|
599
|
+
} else {
|
|
600
|
+
// Create Type.Variable for fields/properties
|
|
601
|
+
const propType = this.checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
602
|
+
const variable: Type.Variable = Object.assign(new NonDraftableType(), {
|
|
603
|
+
kind: Type.Kind.Variable,
|
|
604
|
+
name: prop.getName(),
|
|
605
|
+
owner: classType, // Cyclic reference to the containing class (already in cache)
|
|
606
|
+
type: this.getType(propType), // This will find classType in cache if it's recursive
|
|
607
|
+
annotations: [],
|
|
608
|
+
toJSON: function () {
|
|
609
|
+
return Type.signature(this);
|
|
610
|
+
}
|
|
611
|
+
}) as Type.Variable;
|
|
612
|
+
classType.members.push(variable);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* Note: Object/Class/Interface types are handled in getType() to properly manage circular references
|
|
620
|
+
* This method should only be called for primitive and unknown types
|
|
621
|
+
*/
|
|
622
|
+
private createPrimitiveOrUnknownType(type: ts.Type): Type {
|
|
623
|
+
// Check for literals first
|
|
86
624
|
if (type.isLiteral()) {
|
|
87
625
|
if (type.isNumberLiteral()) {
|
|
88
|
-
return
|
|
626
|
+
return Type.Primitive.Double;
|
|
89
627
|
} else if (type.isStringLiteral()) {
|
|
90
|
-
return
|
|
628
|
+
return Type.Primitive.String;
|
|
91
629
|
}
|
|
92
630
|
}
|
|
93
631
|
|
|
632
|
+
// Check for primitive types
|
|
94
633
|
if (type.flags === ts.TypeFlags.Null) {
|
|
95
|
-
return
|
|
634
|
+
return Type.Primitive.Null;
|
|
96
635
|
} else if (type.flags === ts.TypeFlags.Undefined) {
|
|
97
|
-
return
|
|
636
|
+
return Type.Primitive.None;
|
|
98
637
|
} else if (
|
|
99
638
|
type.flags === ts.TypeFlags.Number ||
|
|
100
639
|
type.flags === ts.TypeFlags.NumberLiteral ||
|
|
101
640
|
type.flags === ts.TypeFlags.NumberLike
|
|
102
641
|
) {
|
|
103
|
-
return
|
|
642
|
+
return Type.Primitive.Double;
|
|
104
643
|
} else if (
|
|
105
644
|
type.flags === ts.TypeFlags.String ||
|
|
106
645
|
type.flags === ts.TypeFlags.StringLiteral ||
|
|
107
646
|
type.flags === ts.TypeFlags.StringLike
|
|
108
647
|
) {
|
|
109
|
-
return
|
|
648
|
+
return Type.Primitive.String;
|
|
110
649
|
} else if (type.flags === ts.TypeFlags.Void) {
|
|
111
|
-
return
|
|
650
|
+
return Type.Primitive.Void;
|
|
112
651
|
} else if (
|
|
113
652
|
type.flags === ts.TypeFlags.BigInt ||
|
|
114
653
|
type.flags === ts.TypeFlags.BigIntLiteral ||
|
|
115
654
|
type.flags === ts.TypeFlags.BigIntLike
|
|
116
655
|
) {
|
|
117
|
-
return
|
|
656
|
+
return Type.Primitive.Long;
|
|
118
657
|
} else if (
|
|
119
658
|
(type.symbol !== undefined && type.symbol === this.regExpSymbol) ||
|
|
120
659
|
this.checker.typeToString(type) === "RegExp"
|
|
121
660
|
) {
|
|
122
|
-
return
|
|
661
|
+
return Type.Primitive.String;
|
|
123
662
|
}
|
|
124
663
|
|
|
125
664
|
/**
|
|
@@ -131,23 +670,19 @@ export class JavaScriptTypeMapping {
|
|
|
131
670
|
type.flags & ts.TypeFlags.BooleanLiteral ||
|
|
132
671
|
type.flags & ts.TypeFlags.BooleanLike
|
|
133
672
|
) {
|
|
134
|
-
return
|
|
673
|
+
return Type.Primitive.Boolean;
|
|
135
674
|
}
|
|
136
675
|
|
|
137
|
-
//
|
|
138
|
-
|
|
139
|
-
|
|
676
|
+
// Check for type aliases that may resolve to primitives
|
|
677
|
+
const symbol = type.getSymbol?.();
|
|
678
|
+
if (symbol && symbol.flags & ts.SymbolFlags.TypeAlias) {
|
|
679
|
+
// Type aliases may resolve to primitive types
|
|
680
|
+
const aliasedType = this.checker.getDeclaredTypeOfSymbol(symbol);
|
|
681
|
+
if (aliasedType !== type) {
|
|
682
|
+
return this.getType(aliasedType);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
140
685
|
|
|
141
|
-
return
|
|
686
|
+
return Type.unknownType;
|
|
142
687
|
}
|
|
143
688
|
}
|
|
144
|
-
|
|
145
|
-
RpcCodecs.registerCodec(JavaType.Kind.Primitive, {
|
|
146
|
-
async rpcSend(after: JavaType.Primitive, q: RpcSendQueue): Promise<void> {
|
|
147
|
-
await q.getAndSend(after, p => p.keyword);
|
|
148
|
-
},
|
|
149
|
-
async rpcReceive(before: JavaType.Primitive, q: RpcReceiveQueue): Promise<JavaType.Primitive> {
|
|
150
|
-
const keyword: string = await q.receive(before.keyword);
|
|
151
|
-
return JavaType.Primitive.fromKeyword(keyword)!;
|
|
152
|
-
}
|
|
153
|
-
});
|