@pikku/inspector 0.6.4 → 0.7.0
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/CHANGELOG.md +10 -0
- package/dist/add-channel.d.ts +12 -2
- package/dist/add-channel.js +336 -109
- package/dist/add-functions.d.ts +7 -0
- package/dist/add-functions.js +260 -0
- package/dist/add-http-route.d.ts +15 -3
- package/dist/add-http-route.js +69 -80
- package/dist/add-schedule.d.ts +1 -1
- package/dist/add-schedule.js +14 -4
- package/dist/events/add-channel.d.ts +1 -0
- package/dist/events/add-channel.js +170 -0
- package/dist/events/add-http-route.d.ts +16 -0
- package/dist/events/add-http-route.js +83 -0
- package/dist/events/add-schedule.d.ts +3 -0
- package/dist/events/add-schedule.js +38 -0
- package/dist/inspector.js +14 -4
- package/dist/types.d.ts +7 -10
- package/dist/utils.d.ts +21 -27
- package/dist/utils.js +631 -211
- package/dist/visit.d.ts +2 -1
- package/dist/visit.js +9 -4
- package/package.json +2 -2
- package/src/add-channel.ts +0 -180
- package/src/add-file-extends-core-type.ts +0 -50
- package/src/add-file-with-config.ts +0 -45
- package/src/add-file-with-factory.ts +0 -65
- package/src/add-http-route.ts +0 -138
- package/src/add-schedule.ts +0 -56
- package/src/does-type-extend-core-type.ts +0 -53
- package/src/get-property-value.ts +0 -84
- package/src/index.ts +0 -4
- package/src/inspector.ts +0 -63
- package/src/types-map.ts +0 -130
- package/src/types.ts +0 -62
- package/src/utils.ts +0 -371
- package/src/visit.ts +0 -57
- package/tsconfig.json +0 -19
- package/tsconfig.tsbuildinfo +0 -1
package/dist/utils.js
CHANGED
|
@@ -1,250 +1,670 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
};
|
|
18
|
-
export const getNamesAndTypes = (checker, typesMap, direction, funcName, type) => {
|
|
19
|
-
const result = {
|
|
20
|
-
names: new Set(),
|
|
21
|
-
types: [],
|
|
22
|
-
};
|
|
23
|
-
const { names, types } = resolveUnionTypes(checker, type);
|
|
24
|
-
const firstName = names[0];
|
|
25
|
-
if (names.length > 1 || (firstName && !isValidVariableName(firstName))) {
|
|
26
|
-
const aliasType = names.join(' | ');
|
|
27
|
-
const aliasName = `${funcName.charAt(0).toUpperCase()}${funcName.slice(1)}${direction}`;
|
|
28
|
-
result.names = new Set([aliasName]);
|
|
29
|
-
result.types = types;
|
|
30
|
-
const references = types
|
|
31
|
-
.map((t) => resolveTypeImports(t, typesMap, true))
|
|
32
|
-
.flat();
|
|
33
|
-
typesMap.addCustomType(aliasName, aliasType, references);
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
const uniqueNames = names
|
|
37
|
-
.map((name, i) => {
|
|
38
|
-
const type = types[i];
|
|
39
|
-
if (!type) {
|
|
40
|
-
throw new Error('TODO: Expected a type here to match name');
|
|
2
|
+
/**
|
|
3
|
+
* Generate a deterministic "anonymous" name for any expression node,
|
|
4
|
+
* but if it's an Identifier pointing to a function, resolve it back
|
|
5
|
+
* to the function's declaration (so you get the true source location).
|
|
6
|
+
*/
|
|
7
|
+
export function makeDeterministicAnonName(start, checker) {
|
|
8
|
+
let node = start;
|
|
9
|
+
let target = start;
|
|
10
|
+
// Handle the case where we're starting with an identifier directly
|
|
11
|
+
if (ts.isIdentifier(node)) {
|
|
12
|
+
const sym = checker.getSymbolAtLocation(node);
|
|
13
|
+
if (sym) {
|
|
14
|
+
let resolvedSym = sym;
|
|
15
|
+
if (resolvedSym.flags & ts.SymbolFlags.Alias) {
|
|
16
|
+
resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym;
|
|
41
17
|
}
|
|
42
|
-
|
|
43
|
-
|
|
18
|
+
const decls = resolvedSym.declarations ?? [];
|
|
19
|
+
if (decls.length > 0) {
|
|
20
|
+
// Start with the declaration, not the reference
|
|
21
|
+
const decl = decls[0];
|
|
22
|
+
// If it's a variable declaration with a function initializer, use the function directly
|
|
23
|
+
if (ts.isVariableDeclaration(decl) &&
|
|
24
|
+
decl.initializer &&
|
|
25
|
+
(ts.isFunctionExpression(decl.initializer) ||
|
|
26
|
+
ts.isArrowFunction(decl.initializer))) {
|
|
27
|
+
target = decl.initializer;
|
|
28
|
+
// Return early - we found the function directly
|
|
29
|
+
const sf = target.getSourceFile();
|
|
30
|
+
const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
|
|
31
|
+
const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
|
|
32
|
+
return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
|
|
33
|
+
}
|
|
34
|
+
// Otherwise continue resolution with the declaration
|
|
35
|
+
node = decl;
|
|
36
|
+
target = decl;
|
|
44
37
|
}
|
|
45
|
-
|
|
46
|
-
})
|
|
47
|
-
.flat();
|
|
48
|
-
result.names = new Set(uniqueNames);
|
|
49
|
-
result.types = types;
|
|
38
|
+
}
|
|
50
39
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
40
|
+
// In an object literal property value, first try to resolve the identifier
|
|
41
|
+
if (ts.isPropertyAssignment(node.parent) &&
|
|
42
|
+
node === node.parent.initializer &&
|
|
43
|
+
ts.isIdentifier(node)) {
|
|
44
|
+
const sym = checker.getSymbolAtLocation(node);
|
|
45
|
+
if (sym) {
|
|
46
|
+
// Process the symbol to find the real declaration
|
|
47
|
+
let resolvedSym = sym;
|
|
48
|
+
if (resolvedSym.flags & ts.SymbolFlags.Alias) {
|
|
49
|
+
resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym;
|
|
50
|
+
}
|
|
51
|
+
const decls = resolvedSym.declarations ?? [];
|
|
52
|
+
if (decls.length > 0) {
|
|
53
|
+
// Found a declaration - use it as our new target
|
|
54
|
+
const decl = decls[0];
|
|
55
|
+
if (!decl) {
|
|
56
|
+
throw new Error('No declaration found');
|
|
57
|
+
}
|
|
58
|
+
// If it's a variable declaration with an initializer function, use that
|
|
59
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
60
|
+
if (ts.isFunctionExpression(decl.initializer) ||
|
|
61
|
+
ts.isArrowFunction(decl.initializer)) {
|
|
62
|
+
target = decl.initializer;
|
|
63
|
+
// Return early - we found the function directly
|
|
64
|
+
const sf = target.getSourceFile();
|
|
65
|
+
const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
|
|
66
|
+
const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
|
|
67
|
+
return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (ts.isFunctionDeclaration(decl)) {
|
|
71
|
+
// Already a function declaration
|
|
72
|
+
target = decl;
|
|
73
|
+
// Return early
|
|
74
|
+
const sf = target.getSourceFile();
|
|
75
|
+
const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
|
|
76
|
+
const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
|
|
77
|
+
return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
|
|
78
|
+
}
|
|
79
|
+
// If we didn't return early, continue with this declaration
|
|
80
|
+
node = decl;
|
|
81
|
+
target = decl;
|
|
79
82
|
}
|
|
80
83
|
}
|
|
81
84
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
const seen = new Set();
|
|
86
|
+
for (let depth = 0; depth < 10; depth++) {
|
|
87
|
+
if (!ts.isIdentifier(node) || seen.has(node))
|
|
88
|
+
break;
|
|
89
|
+
seen.add(node);
|
|
90
|
+
let sym = checker.getSymbolAtLocation(node);
|
|
91
|
+
if (!sym)
|
|
92
|
+
break;
|
|
93
|
+
if (sym.flags & ts.SymbolFlags.Alias) {
|
|
94
|
+
sym = checker.getAliasedSymbol(sym) ?? sym;
|
|
87
95
|
}
|
|
96
|
+
const allDecls = sym.declarations ?? [];
|
|
97
|
+
// prefer real .ts/.tsx implementation files
|
|
98
|
+
const implDecls = allDecls.filter((d) => !d.getSourceFile().isDeclarationFile);
|
|
99
|
+
const decls = implDecls.length ? implDecls : allDecls;
|
|
100
|
+
let didResolve = false;
|
|
101
|
+
for (const decl of decls) {
|
|
102
|
+
// 1) direct function foo() {} or function-expression
|
|
103
|
+
if (ts.isFunctionDeclaration(decl) ||
|
|
104
|
+
ts.isFunctionExpression(decl) ||
|
|
105
|
+
ts.isArrowFunction(decl)) {
|
|
106
|
+
target = decl;
|
|
107
|
+
didResolve = true;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
// 2) const foo = () => {} or foo = function() {}
|
|
111
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
112
|
+
const init = decl.initializer;
|
|
113
|
+
if (ts.isFunctionExpression(init) || ts.isArrowFunction(init)) {
|
|
114
|
+
target = init;
|
|
115
|
+
didResolve = true;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
// 2b) const foo = bar; (follow the next identifier)
|
|
119
|
+
if (ts.isIdentifier(init)) {
|
|
120
|
+
node = init;
|
|
121
|
+
target = init;
|
|
122
|
+
didResolve = true;
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// 3) Handle shorthand property assignments: { foo } (equivalent to { foo: foo })
|
|
127
|
+
if (ts.isShorthandPropertyAssignment(decl)) {
|
|
128
|
+
// Get the symbol for the shorthand property
|
|
129
|
+
const shorthandSym = checker.getShorthandAssignmentValueSymbol(decl);
|
|
130
|
+
if (shorthandSym &&
|
|
131
|
+
shorthandSym.declarations &&
|
|
132
|
+
shorthandSym.declarations.length > 0) {
|
|
133
|
+
// Use the first declaration as our new target
|
|
134
|
+
const shorthandDecl = shorthandSym.declarations[0];
|
|
135
|
+
target = shorthandDecl;
|
|
136
|
+
if (!shorthandDecl) {
|
|
137
|
+
throw new Error('No shorthand declaration found');
|
|
138
|
+
}
|
|
139
|
+
// Check the type of declaration and extract the appropriate identifier to continue resolving
|
|
140
|
+
if (ts.isVariableDeclaration(shorthandDecl) &&
|
|
141
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
142
|
+
node = shorthandDecl.name;
|
|
143
|
+
didResolve = true;
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
else if (ts.isFunctionDeclaration(shorthandDecl) &&
|
|
147
|
+
shorthandDecl.name &&
|
|
148
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
149
|
+
node = shorthandDecl.name;
|
|
150
|
+
didResolve = true;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
else if (ts.isParameter(shorthandDecl) &&
|
|
154
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
155
|
+
node = shorthandDecl.name;
|
|
156
|
+
didResolve = true;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
else if (ts.isPropertyDeclaration(shorthandDecl) &&
|
|
160
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
161
|
+
node = shorthandDecl.name;
|
|
162
|
+
didResolve = true;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
else if (ts.isMethodDeclaration(shorthandDecl) &&
|
|
166
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
167
|
+
node = shorthandDecl.name;
|
|
168
|
+
didResolve = true;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// 4) Handle method declarations in classes/objects
|
|
174
|
+
if (ts.isMethodDeclaration(decl)) {
|
|
175
|
+
target = decl;
|
|
176
|
+
didResolve = true;
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
// you can add more cases here if your setup uses imports, etc.
|
|
180
|
+
}
|
|
181
|
+
if (!didResolve)
|
|
182
|
+
break;
|
|
88
183
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
184
|
+
const sf = target.getSourceFile();
|
|
185
|
+
const file = sf.fileName.replace(/[^A-Za-z0-9_]/g, '_');
|
|
186
|
+
const { line, character } = ts.getLineAndCharacterOfPosition(sf, target.getStart());
|
|
187
|
+
return `pikkuFn_${file}_L${line + 1}C${character + 1}`;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Updated function to extract and prioritize function names correctly
|
|
191
|
+
* This function follows the priority:
|
|
192
|
+
* 1. Object with a name property
|
|
193
|
+
* 2. Exported name
|
|
194
|
+
* 3. Fallback to deterministic name
|
|
195
|
+
*/
|
|
196
|
+
export function extractFunctionName(callExpr, checker) {
|
|
197
|
+
const parent = callExpr.parent;
|
|
198
|
+
// Initialize the result
|
|
199
|
+
const result = {
|
|
200
|
+
pikkuFuncName: '', // Will be populated later
|
|
201
|
+
name: '', // This will hold our "best" name based on priority
|
|
202
|
+
exportedName: null,
|
|
203
|
+
functionName: null,
|
|
204
|
+
propertyName: null,
|
|
205
|
+
};
|
|
206
|
+
// Special case for addHTTPRoute: if this is an identifier within an object literal,
|
|
207
|
+
// it might be coming from the HTTP route handling flow
|
|
208
|
+
if (ts.isIdentifier(callExpr) &&
|
|
209
|
+
callExpr.parent &&
|
|
210
|
+
ts.isPropertyAssignment(callExpr.parent)) {
|
|
211
|
+
// Try to handle the special case for HTTP route functions
|
|
212
|
+
const sym = checker.getSymbolAtLocation(callExpr);
|
|
213
|
+
if (sym) {
|
|
214
|
+
let resolvedSym = sym;
|
|
215
|
+
if (resolvedSym.flags & ts.SymbolFlags.Alias) {
|
|
216
|
+
resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym;
|
|
217
|
+
}
|
|
218
|
+
const decls = resolvedSym.declarations ?? [];
|
|
219
|
+
if (decls.length > 0) {
|
|
220
|
+
const decl = decls[0];
|
|
221
|
+
// Check if the declaration is a variable that uses pikkuSessionlessFunc
|
|
222
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
223
|
+
if (ts.isCallExpression(decl.initializer) &&
|
|
224
|
+
ts.isIdentifier(decl.initializer.expression) &&
|
|
225
|
+
decl.initializer.expression.text.startsWith('pikku')) {
|
|
226
|
+
const args = decl.initializer.arguments;
|
|
227
|
+
const firstArg = args[0];
|
|
228
|
+
if (firstArg &&
|
|
229
|
+
(ts.isArrowFunction(firstArg) ||
|
|
230
|
+
ts.isFunctionExpression(firstArg))) {
|
|
231
|
+
// Use the function directly for position calculation
|
|
232
|
+
result.pikkuFuncName = makeDeterministicAnonName(firstArg, checker);
|
|
233
|
+
// Continue with name extraction
|
|
234
|
+
if (ts.isIdentifier(parent.name)) {
|
|
235
|
+
result.propertyName = parent.name.text;
|
|
236
|
+
}
|
|
237
|
+
// Check if the variable is exported
|
|
238
|
+
if (ts.isVariableDeclaration(decl) &&
|
|
239
|
+
isNamedExport(decl) &&
|
|
240
|
+
ts.isIdentifier(decl.name)) {
|
|
241
|
+
result.exportedName = decl.name.text;
|
|
242
|
+
}
|
|
243
|
+
else if (ts.isIdentifier(decl.name)) {
|
|
244
|
+
// If not exported, still capture the variable name
|
|
245
|
+
result.functionName = decl.name.text;
|
|
246
|
+
}
|
|
247
|
+
// Apply name priority logic
|
|
248
|
+
populateNameByPriority(result);
|
|
249
|
+
return result;
|
|
115
250
|
}
|
|
116
251
|
}
|
|
117
|
-
types.push(uniqueName);
|
|
118
252
|
}
|
|
119
253
|
}
|
|
120
254
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
255
|
+
}
|
|
256
|
+
// First, figure out what function we're really dealing with
|
|
257
|
+
let mainFunc = callExpr;
|
|
258
|
+
let originalCallExpr = callExpr; // Keep track of the original call expression for name extraction
|
|
259
|
+
// For direct pikku function calls where callExpr is the call expression itself
|
|
260
|
+
if (ts.isCallExpression(callExpr)) {
|
|
261
|
+
const { expression, arguments: args } = callExpr;
|
|
262
|
+
// Check if this is a pikku function call (pikkuFunc, pikkuSessionlessFunc, etc)
|
|
263
|
+
if (ts.isIdentifier(expression) && expression.text.startsWith('pikku')) {
|
|
264
|
+
// Check for object with 'name' property in first argument
|
|
265
|
+
const firstArg = args[0];
|
|
266
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
267
|
+
for (const prop of firstArg.properties) {
|
|
268
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
269
|
+
ts.isIdentifier(prop.name) &&
|
|
270
|
+
prop.name.text === 'name' &&
|
|
271
|
+
ts.isStringLiteral(prop.initializer)) {
|
|
272
|
+
// Priority 1: Object with name property
|
|
273
|
+
result.functionName = prop.initializer.text;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
125
277
|
}
|
|
126
|
-
//
|
|
127
|
-
if (
|
|
128
|
-
|
|
278
|
+
// Special handling for pikkuSessionlessFunc pattern - use the arrow function directly
|
|
279
|
+
if (expression.text.startsWith('pikku')) {
|
|
280
|
+
if (args.length > 0) {
|
|
281
|
+
const firstArg = args[0];
|
|
282
|
+
if (ts.isArrowFunction(firstArg) ||
|
|
283
|
+
ts.isFunctionExpression(firstArg)) {
|
|
284
|
+
mainFunc = firstArg; // Use the arrow function directly instead of the call expression
|
|
285
|
+
}
|
|
286
|
+
}
|
|
129
287
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
288
|
+
}
|
|
289
|
+
// Handle object initializer with a func property (for both patterns)
|
|
290
|
+
if (args.length > 0) {
|
|
291
|
+
const firstArg = args[0];
|
|
292
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
293
|
+
// Look for func property in the object
|
|
294
|
+
for (const prop of firstArg.properties) {
|
|
295
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
296
|
+
ts.isIdentifier(prop.name) &&
|
|
297
|
+
prop.name.text === 'func') {
|
|
298
|
+
if (ts.isIdentifier(prop.initializer)) {
|
|
299
|
+
// func: someFunction - resolve the function
|
|
300
|
+
const funcSym = checker.getSymbolAtLocation(prop.initializer);
|
|
301
|
+
if (funcSym) {
|
|
302
|
+
let resolvedFuncSym = funcSym;
|
|
303
|
+
if (resolvedFuncSym.flags & ts.SymbolFlags.Alias) {
|
|
304
|
+
resolvedFuncSym =
|
|
305
|
+
checker.getAliasedSymbol(resolvedFuncSym) ?? resolvedFuncSym;
|
|
306
|
+
}
|
|
307
|
+
const funcDecls = resolvedFuncSym.declarations ?? [];
|
|
308
|
+
if (funcDecls.length > 0) {
|
|
309
|
+
const funcDecl = funcDecls[0];
|
|
310
|
+
// Check if it's a pikkuSessionlessFunc
|
|
311
|
+
if (ts.isVariableDeclaration(funcDecl) &&
|
|
312
|
+
funcDecl.initializer) {
|
|
313
|
+
if (ts.isCallExpression(funcDecl.initializer) &&
|
|
314
|
+
ts.isIdentifier(funcDecl.initializer.expression) &&
|
|
315
|
+
funcDecl.initializer.expression.text.startsWith('pikku')) {
|
|
316
|
+
const funcArgs = funcDecl.initializer.arguments;
|
|
317
|
+
const firstArg = funcArgs[0];
|
|
318
|
+
if (firstArg &&
|
|
319
|
+
(ts.isArrowFunction(firstArg) ||
|
|
320
|
+
ts.isFunctionExpression(firstArg))) {
|
|
321
|
+
mainFunc = firstArg;
|
|
322
|
+
// Check if the variable is exported
|
|
323
|
+
if (isNamedExport(funcDecl) &&
|
|
324
|
+
ts.isIdentifier(funcDecl.name)) {
|
|
325
|
+
result.exportedName = funcDecl.name.text;
|
|
326
|
+
}
|
|
327
|
+
else if (ts.isIdentifier(funcDecl.name)) {
|
|
328
|
+
// If not exported, still capture the variable name
|
|
329
|
+
result.functionName = funcDecl.name.text;
|
|
330
|
+
}
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (ts.isFunctionExpression(funcDecl.initializer) ||
|
|
335
|
+
ts.isArrowFunction(funcDecl.initializer)) {
|
|
336
|
+
mainFunc = funcDecl.initializer;
|
|
337
|
+
// Check if the variable is exported
|
|
338
|
+
if (isNamedExport(funcDecl) &&
|
|
339
|
+
ts.isIdentifier(funcDecl.name)) {
|
|
340
|
+
result.exportedName = funcDecl.name.text;
|
|
341
|
+
}
|
|
342
|
+
else if (ts.isIdentifier(funcDecl.name)) {
|
|
343
|
+
// If not exported, still capture the variable name
|
|
344
|
+
result.functionName = funcDecl.name.text;
|
|
345
|
+
}
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else if (ts.isFunctionDeclaration(funcDecl)) {
|
|
350
|
+
mainFunc = funcDecl;
|
|
351
|
+
// Check if the function is exported
|
|
352
|
+
if (funcDecl.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
353
|
+
funcDecl.name &&
|
|
354
|
+
ts.isIdentifier(funcDecl.name)) {
|
|
355
|
+
result.exportedName = funcDecl.name.text;
|
|
356
|
+
}
|
|
357
|
+
else if (funcDecl.name &&
|
|
358
|
+
ts.isIdentifier(funcDecl.name)) {
|
|
359
|
+
// If not exported, still capture the function name
|
|
360
|
+
result.functionName = funcDecl.name.text;
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
// If we can't resolve the symbol, use the identifier itself
|
|
368
|
+
mainFunc = prop.initializer;
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
else if (ts.isFunctionExpression(prop.initializer) ||
|
|
373
|
+
ts.isArrowFunction(prop.initializer)) {
|
|
374
|
+
// func: () => {} or func: function() {} - use directly
|
|
375
|
+
mainFunc = prop.initializer;
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
else if (ts.isShorthandPropertyAssignment(prop) &&
|
|
380
|
+
ts.isIdentifier(prop.name) &&
|
|
381
|
+
prop.name.text === 'func') {
|
|
382
|
+
// Handle func shorthand property
|
|
383
|
+
const shorthandSym = checker.getShorthandAssignmentValueSymbol(prop);
|
|
384
|
+
if (shorthandSym &&
|
|
385
|
+
shorthandSym.declarations &&
|
|
386
|
+
shorthandSym.declarations.length > 0) {
|
|
387
|
+
const shorthandDecl = shorthandSym.declarations[0];
|
|
388
|
+
if (!shorthandDecl) {
|
|
389
|
+
throw new Error('No shorthand declaration found');
|
|
390
|
+
}
|
|
391
|
+
if (ts.isVariableDeclaration(shorthandDecl) &&
|
|
392
|
+
shorthandDecl.initializer) {
|
|
393
|
+
if (ts.isCallExpression(shorthandDecl.initializer) &&
|
|
394
|
+
ts.isIdentifier(shorthandDecl.initializer.expression) &&
|
|
395
|
+
shorthandDecl.initializer.expression.text.startsWith('pikku')) {
|
|
396
|
+
const args = shorthandDecl.initializer.arguments;
|
|
397
|
+
const firstArg = args[0];
|
|
398
|
+
if (firstArg &&
|
|
399
|
+
(ts.isArrowFunction(firstArg) ||
|
|
400
|
+
ts.isFunctionExpression(firstArg))) {
|
|
401
|
+
mainFunc = firstArg;
|
|
402
|
+
// Check if the variable is exported
|
|
403
|
+
if (isNamedExport(shorthandDecl) &&
|
|
404
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
405
|
+
result.exportedName = shorthandDecl.name.text;
|
|
406
|
+
}
|
|
407
|
+
else if (ts.isIdentifier(shorthandDecl.name)) {
|
|
408
|
+
// If not exported, still capture the variable name
|
|
409
|
+
result.functionName = shorthandDecl.name.text;
|
|
410
|
+
}
|
|
411
|
+
break;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
else if (ts.isFunctionExpression(shorthandDecl.initializer) ||
|
|
415
|
+
ts.isArrowFunction(shorthandDecl.initializer)) {
|
|
416
|
+
mainFunc = shorthandDecl.initializer;
|
|
417
|
+
// Check if the variable is exported
|
|
418
|
+
if (isNamedExport(shorthandDecl) &&
|
|
419
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
420
|
+
result.exportedName = shorthandDecl.name.text;
|
|
421
|
+
}
|
|
422
|
+
else if (ts.isIdentifier(shorthandDecl.name)) {
|
|
423
|
+
// If not exported, still capture the variable name
|
|
424
|
+
result.functionName = shorthandDecl.name.text;
|
|
425
|
+
}
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
else if (ts.isFunctionDeclaration(shorthandDecl)) {
|
|
430
|
+
mainFunc = shorthandDecl;
|
|
431
|
+
// Check if the function is exported
|
|
432
|
+
if (shorthandDecl.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
433
|
+
shorthandDecl.name &&
|
|
434
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
435
|
+
result.exportedName = shorthandDecl.name.text;
|
|
436
|
+
}
|
|
437
|
+
else if (shorthandDecl.name &&
|
|
438
|
+
ts.isIdentifier(shorthandDecl.name)) {
|
|
439
|
+
// If not exported, still capture the function name
|
|
440
|
+
result.functionName = shorthandDecl.name.text;
|
|
441
|
+
}
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
135
447
|
}
|
|
136
448
|
}
|
|
137
|
-
};
|
|
138
|
-
visitType(type);
|
|
139
|
-
return types;
|
|
140
|
-
};
|
|
141
|
-
export const getPropertyAssignment = (obj, name) => {
|
|
142
|
-
const property = obj.properties.find((p) => (ts.isPropertyAssignment(p) || ts.isShorthandPropertyAssignment(p)) &&
|
|
143
|
-
ts.isIdentifier(p.name) &&
|
|
144
|
-
p.name.text === name);
|
|
145
|
-
if (!property) {
|
|
146
|
-
console.error(`Missing property '${name}' in object`);
|
|
147
|
-
return null;
|
|
148
449
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
450
|
+
// Handle direct identifier case
|
|
451
|
+
else if (ts.isIdentifier(callExpr)) {
|
|
452
|
+
const sym = checker.getSymbolAtLocation(callExpr);
|
|
453
|
+
if (sym) {
|
|
454
|
+
let resolvedSym = sym;
|
|
455
|
+
if (resolvedSym.flags & ts.SymbolFlags.Alias) {
|
|
456
|
+
resolvedSym = checker.getAliasedSymbol(resolvedSym) ?? resolvedSym;
|
|
457
|
+
}
|
|
458
|
+
const decls = resolvedSym.declarations ?? [];
|
|
459
|
+
if (decls.length > 0) {
|
|
460
|
+
const decl = decls[0];
|
|
461
|
+
if (!decl) {
|
|
462
|
+
throw new Error('No declaration found');
|
|
463
|
+
}
|
|
464
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
465
|
+
if (ts.isCallExpression(decl.initializer) &&
|
|
466
|
+
ts.isIdentifier(decl.initializer.expression) &&
|
|
467
|
+
decl.initializer.expression.text.startsWith('pikku')) {
|
|
468
|
+
// Check for object with 'name' property in first argument
|
|
469
|
+
const firstArg = decl.initializer.arguments[0];
|
|
470
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
471
|
+
for (const prop of firstArg.properties) {
|
|
472
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
473
|
+
ts.isIdentifier(prop.name) &&
|
|
474
|
+
prop.name.text === 'name' &&
|
|
475
|
+
ts.isStringLiteral(prop.initializer)) {
|
|
476
|
+
// Priority 1: Object with name property
|
|
477
|
+
result.functionName = prop.initializer.text;
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
if (decl.initializer.expression.text.startsWith('pikku')) {
|
|
483
|
+
if (firstArg &&
|
|
484
|
+
(ts.isArrowFunction(firstArg) ||
|
|
485
|
+
ts.isFunctionExpression(firstArg))) {
|
|
486
|
+
mainFunc = firstArg;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Check if the variable is exported
|
|
490
|
+
if (isNamedExport(decl) && ts.isIdentifier(decl.name)) {
|
|
491
|
+
result.exportedName = decl.name.text;
|
|
492
|
+
}
|
|
493
|
+
else if (ts.isIdentifier(decl.name)) {
|
|
494
|
+
// If not explicitly set by name property above, set functionName
|
|
495
|
+
if (!result.functionName) {
|
|
496
|
+
result.functionName = decl.name.text;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
else if (ts.isFunctionExpression(decl.initializer) ||
|
|
501
|
+
ts.isArrowFunction(decl.initializer)) {
|
|
502
|
+
mainFunc = decl.initializer;
|
|
503
|
+
// Check if the variable is exported
|
|
504
|
+
if (isNamedExport(decl) && ts.isIdentifier(decl.name)) {
|
|
505
|
+
result.exportedName = decl.name.text;
|
|
506
|
+
}
|
|
507
|
+
else if (ts.isIdentifier(decl.name)) {
|
|
508
|
+
result.functionName = decl.name.text;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else if (ts.isFunctionDeclaration(decl)) {
|
|
513
|
+
mainFunc = decl;
|
|
514
|
+
// Check if the function is exported
|
|
515
|
+
if (decl.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
516
|
+
decl.name &&
|
|
517
|
+
ts.isIdentifier(decl.name)) {
|
|
518
|
+
result.exportedName = decl.name.text;
|
|
519
|
+
}
|
|
520
|
+
else if (decl.name && ts.isIdentifier(decl.name)) {
|
|
521
|
+
result.functionName = decl.name.text;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
158
524
|
}
|
|
159
525
|
}
|
|
160
|
-
return types.length > 0 ? types : null;
|
|
161
526
|
}
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
527
|
+
// Now generate the deterministic function name based on the resolved function
|
|
528
|
+
result.pikkuFuncName = makeDeterministicAnonName(mainFunc, checker);
|
|
529
|
+
// Continue with regular name extraction for remaining cases
|
|
530
|
+
// 1) const foo = pikkuFunc(...)
|
|
531
|
+
if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) {
|
|
532
|
+
if (isNamedExport(parent)) {
|
|
533
|
+
result.exportedName = parent.name.text;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
// Still capture the variable name even if not exported
|
|
537
|
+
result.functionName = parent.name.text;
|
|
168
538
|
}
|
|
169
539
|
}
|
|
170
|
-
//
|
|
171
|
-
if (
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
175
|
-
};
|
|
176
|
-
export const getFunctionTypes = (checker, obj, { typesMap, funcName, subFunctionName = funcName, inputIndex, outputIndex, }) => {
|
|
177
|
-
const result = {
|
|
178
|
-
inputTypes: [],
|
|
179
|
-
inputs: null,
|
|
180
|
-
outputTypes: [],
|
|
181
|
-
outputs: null,
|
|
182
|
-
type: null,
|
|
183
|
-
};
|
|
184
|
-
const property = getPropertyAssignment(obj, subFunctionName);
|
|
185
|
-
if (!property) {
|
|
186
|
-
return result;
|
|
540
|
+
// 2) { foo: pikkuFunc(...) }
|
|
541
|
+
else if (ts.isPropertyAssignment(parent) && ts.isIdentifier(parent.name)) {
|
|
542
|
+
result.propertyName = parent.name.text;
|
|
187
543
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (symbol) {
|
|
193
|
-
type = checker.getTypeOfSymbolAtLocation(symbol, property);
|
|
194
|
-
if (funcName === 'func') {
|
|
195
|
-
funcName = symbol.name;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
544
|
+
// 2b) Handle shorthand property { foo } - which is equivalent to { foo: foo }
|
|
545
|
+
else if (ts.isShorthandPropertyAssignment(parent) &&
|
|
546
|
+
ts.isIdentifier(parent.name)) {
|
|
547
|
+
result.propertyName = parent.name.text;
|
|
198
548
|
}
|
|
199
|
-
// Handle
|
|
200
|
-
else if (ts.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
funcName = property.initializer.getText();
|
|
549
|
+
// 3) Handle any remaining cases for pikkuFunc({ name: '…', func: … })
|
|
550
|
+
else if (ts.isCallExpression(originalCallExpr)) {
|
|
551
|
+
const firstArg = originalCallExpr.arguments[0];
|
|
552
|
+
if (firstArg && ts.isObjectLiteralExpression(firstArg)) {
|
|
553
|
+
for (const prop of firstArg.properties) {
|
|
554
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
555
|
+
ts.isIdentifier(prop.name) &&
|
|
556
|
+
prop.name.text === 'name' &&
|
|
557
|
+
ts.isStringLiteral(prop.initializer) &&
|
|
558
|
+
!result.functionName // Only set if not already set
|
|
559
|
+
) {
|
|
560
|
+
result.functionName = prop.initializer.text;
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
214
563
|
}
|
|
215
564
|
}
|
|
216
565
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
566
|
+
// Apply name priority logic
|
|
567
|
+
populateNameByPriority(result);
|
|
568
|
+
return result;
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Helper function to populate the 'name' field based on priority
|
|
572
|
+
*/
|
|
573
|
+
function populateNameByPriority(result) {
|
|
574
|
+
// Priority 1: If we have a functionName (from name property or variable name), use that
|
|
575
|
+
if (result.functionName) {
|
|
576
|
+
result.name = result.functionName;
|
|
220
577
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (!typeArguments || typeArguments.length === 0) {
|
|
225
|
-
// This is the case for inline functions. In this case we would want to
|
|
226
|
-
// get the types from the second argument of the function...
|
|
227
|
-
console.error(`\x1b[31m• No generic type arguments found for ${funcName}. Support for inline functions is not yet implemented.\x1b[0m`);
|
|
228
|
-
return result;
|
|
578
|
+
// Priority 2: If we have an exported name, use that
|
|
579
|
+
else if (result.exportedName) {
|
|
580
|
+
result.name = result.exportedName;
|
|
229
581
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
result.
|
|
233
|
-
result.inputTypes = types;
|
|
582
|
+
// Priority 3: If we have a property name, use that
|
|
583
|
+
else if (result.propertyName) {
|
|
584
|
+
result.name = result.propertyName;
|
|
234
585
|
}
|
|
586
|
+
// Fallback: Use the deterministic name, but we could shorten it in the future
|
|
235
587
|
else {
|
|
236
|
-
|
|
588
|
+
// For now, just use the full pikkuFuncName
|
|
589
|
+
result.name = result.pikkuFuncName;
|
|
590
|
+
// Alternative: extract just the filename and line/column from pikkuFuncName
|
|
591
|
+
// const nameParts = result.pikkuFuncName.split('_');
|
|
592
|
+
// if (nameParts.length >= 3) {
|
|
593
|
+
// // Extract just filename + line/column info
|
|
594
|
+
// result.name = `${nameParts[1]}_${nameParts[2]}`;
|
|
595
|
+
// }
|
|
237
596
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Helper function to check if a variable declaration is a named export
|
|
600
|
+
*/
|
|
601
|
+
function isNamedExport(declaration) {
|
|
602
|
+
let parent = declaration.parent;
|
|
603
|
+
if (!parent)
|
|
604
|
+
return false;
|
|
605
|
+
// Check if it's part of a variable declaration list
|
|
606
|
+
if (ts.isVariableDeclarationList(parent)) {
|
|
607
|
+
parent = parent.parent;
|
|
608
|
+
if (!parent)
|
|
609
|
+
return false;
|
|
610
|
+
// Check if it's in an export declaration
|
|
611
|
+
if (ts.isVariableStatement(parent)) {
|
|
612
|
+
return (parent.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ??
|
|
613
|
+
false);
|
|
614
|
+
}
|
|
245
615
|
}
|
|
246
|
-
return
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
// Until here
|
|
619
|
+
export const extractTypeKeys = (type) => {
|
|
620
|
+
return type.getProperties().map((symbol) => symbol.getName());
|
|
247
621
|
};
|
|
622
|
+
export function getPropertyAssignmentInitializer(obj, propName, followShorthand = false, checker) {
|
|
623
|
+
for (const prop of obj.properties) {
|
|
624
|
+
// ① foo: () => {}
|
|
625
|
+
if (ts.isPropertyAssignment(prop) &&
|
|
626
|
+
ts.isIdentifier(prop.name) &&
|
|
627
|
+
prop.name.text === propName) {
|
|
628
|
+
return prop.initializer;
|
|
629
|
+
}
|
|
630
|
+
// ② foo() { … }
|
|
631
|
+
if (ts.isMethodDeclaration(prop) &&
|
|
632
|
+
ts.isIdentifier(prop.name) &&
|
|
633
|
+
prop.name.text === propName) {
|
|
634
|
+
return prop.name; // the method node *is* the function
|
|
635
|
+
}
|
|
636
|
+
// ③ { foo } (shorthand)
|
|
637
|
+
if (followShorthand &&
|
|
638
|
+
ts.isShorthandPropertyAssignment(prop) &&
|
|
639
|
+
prop.name.text === propName) {
|
|
640
|
+
if (!checker)
|
|
641
|
+
return prop.name; // best effort without a checker
|
|
642
|
+
let sym = checker.getSymbolAtLocation(prop.name);
|
|
643
|
+
if (sym && sym.flags & ts.SymbolFlags.Alias) {
|
|
644
|
+
sym = checker.getAliasedSymbol(sym);
|
|
645
|
+
}
|
|
646
|
+
const decl = sym?.declarations?.[0];
|
|
647
|
+
// const foo = () => {}
|
|
648
|
+
if (decl &&
|
|
649
|
+
ts.isVariableDeclaration(decl) &&
|
|
650
|
+
decl.initializer &&
|
|
651
|
+
(ts.isArrowFunction(decl.initializer) ||
|
|
652
|
+
ts.isFunctionExpression(decl.initializer))) {
|
|
653
|
+
return decl.initializer;
|
|
654
|
+
}
|
|
655
|
+
// function foo() {}
|
|
656
|
+
if (decl &&
|
|
657
|
+
(ts.isFunctionDeclaration(decl) ||
|
|
658
|
+
ts.isArrowFunction(decl) ||
|
|
659
|
+
ts.isFunctionExpression(decl))) {
|
|
660
|
+
return decl;
|
|
661
|
+
}
|
|
662
|
+
// fallback – just give back the identifier
|
|
663
|
+
return prop.name;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
return undefined;
|
|
667
|
+
}
|
|
248
668
|
export const matchesFilters = (filters, params, meta) => {
|
|
249
669
|
if (Object.keys(filters).length === 0 || filters.tags?.length === 0) {
|
|
250
670
|
return true;
|