@flisk/analyze-tracking 0.7.6 → 0.8.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/README.md +29 -21
- package/bin/cli.js +1 -0
- package/package.json +1 -1
- package/src/analyze/go/astTraversal.js +22 -22
- package/src/analyze/go/eventExtractor.js +10 -7
- package/src/analyze/go/index.js +39 -19
- package/src/analyze/go/propertyExtractor.js +25 -5
- package/src/analyze/go/trackingExtractor.js +5 -5
- package/src/analyze/index.js +9 -6
- package/src/analyze/javascript/detectors/analytics-source.js +55 -2
- package/src/analyze/javascript/extractors/event-extractor.js +69 -2
- package/src/analyze/javascript/index.js +14 -8
- package/src/analyze/javascript/parser.js +87 -14
- package/src/analyze/python/index.js +32 -26
- package/src/analyze/python/pythonTrackingAnalyzer.py +113 -39
- package/src/analyze/ruby/extractors.js +46 -10
- package/src/analyze/ruby/index.js +14 -7
- package/src/analyze/ruby/visitor.js +24 -7
- package/src/analyze/typescript/extractors/event-extractor.js +75 -2
- package/src/analyze/typescript/index.js +16 -10
- package/src/analyze/typescript/parser.js +37 -14
- package/src/analyze/utils/customFunctionParser.js +55 -0
- package/src/index.js +2 -2
|
@@ -16,7 +16,7 @@ let parse = null;
|
|
|
16
16
|
* @returns {Promise<Array>} Array of tracking events found in the file
|
|
17
17
|
* @throws {Error} If the file cannot be read or parsed
|
|
18
18
|
*/
|
|
19
|
-
async function analyzeRubyFile(filePath,
|
|
19
|
+
async function analyzeRubyFile(filePath, customFunctionSignatures = null) {
|
|
20
20
|
// Lazy load the Ruby Prism parser
|
|
21
21
|
if (!parse) {
|
|
22
22
|
const { loadPrism } = await import('@ruby/prism');
|
|
@@ -26,21 +26,28 @@ async function analyzeRubyFile(filePath, customFunction) {
|
|
|
26
26
|
try {
|
|
27
27
|
// Read the file content
|
|
28
28
|
const code = fs.readFileSync(filePath, 'utf8');
|
|
29
|
-
|
|
30
|
-
// Parse the Ruby code into an AST
|
|
29
|
+
|
|
30
|
+
// Parse the Ruby code into an AST once
|
|
31
31
|
let ast;
|
|
32
32
|
try {
|
|
33
33
|
ast = await parse(code);
|
|
34
34
|
} catch (parseError) {
|
|
35
35
|
console.error(`Error parsing file ${filePath}:`, parseError.message);
|
|
36
|
-
return [];
|
|
36
|
+
return [];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
//
|
|
40
|
-
const visitor = new TrackingVisitor(code, filePath,
|
|
39
|
+
// Single visitor pass covering all custom configs
|
|
40
|
+
const visitor = new TrackingVisitor(code, filePath, customFunctionSignatures || []);
|
|
41
41
|
const events = await visitor.analyze(ast);
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
// Deduplicate events
|
|
44
|
+
const unique = new Map();
|
|
45
|
+
for (const evt of events) {
|
|
46
|
+
const key = `${evt.source}|${evt.eventName}|${evt.line}|${evt.functionName}`;
|
|
47
|
+
if (!unique.has(key)) unique.set(key, evt);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Array.from(unique.values());
|
|
44
51
|
|
|
45
52
|
} catch (fileError) {
|
|
46
53
|
console.error(`Error reading or processing file ${filePath}:`, fileError.message);
|
|
@@ -8,10 +8,10 @@ const { extractEventName, extractProperties } = require('./extractors');
|
|
|
8
8
|
const { findWrappingFunction, traverseNode, getLineNumber } = require('./traversal');
|
|
9
9
|
|
|
10
10
|
class TrackingVisitor {
|
|
11
|
-
constructor(code, filePath,
|
|
11
|
+
constructor(code, filePath, customConfigs = []) {
|
|
12
12
|
this.code = code;
|
|
13
13
|
this.filePath = filePath;
|
|
14
|
-
this.
|
|
14
|
+
this.customConfigs = Array.isArray(customConfigs) ? customConfigs : [];
|
|
15
15
|
this.events = [];
|
|
16
16
|
}
|
|
17
17
|
|
|
@@ -22,10 +22,27 @@ class TrackingVisitor {
|
|
|
22
22
|
*/
|
|
23
23
|
async processCallNode(node, ancestors) {
|
|
24
24
|
try {
|
|
25
|
-
|
|
25
|
+
let matchedConfig = null;
|
|
26
|
+
let source = null;
|
|
27
|
+
|
|
28
|
+
// Try to match any custom config first
|
|
29
|
+
for (const cfg of this.customConfigs) {
|
|
30
|
+
if (!cfg) continue;
|
|
31
|
+
if (detectSource(node, cfg.functionName) === 'custom') {
|
|
32
|
+
matchedConfig = cfg;
|
|
33
|
+
source = 'custom';
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// If no custom match, attempt built-in providers
|
|
39
|
+
if (!source) {
|
|
40
|
+
source = detectSource(node, null);
|
|
41
|
+
}
|
|
42
|
+
|
|
26
43
|
if (!source) return;
|
|
27
44
|
|
|
28
|
-
const eventName = extractEventName(node, source);
|
|
45
|
+
const eventName = extractEventName(node, source, matchedConfig);
|
|
29
46
|
if (!eventName) return;
|
|
30
47
|
|
|
31
48
|
const line = getLineNumber(this.code, node.location);
|
|
@@ -33,13 +50,13 @@ class TrackingVisitor {
|
|
|
33
50
|
// For module-scoped custom functions, use the custom function name as the functionName
|
|
34
51
|
// For simple custom functions, use the wrapping function name
|
|
35
52
|
let functionName;
|
|
36
|
-
if (source === 'custom' &&
|
|
37
|
-
functionName =
|
|
53
|
+
if (source === 'custom' && matchedConfig && matchedConfig.functionName.includes('.')) {
|
|
54
|
+
functionName = matchedConfig.functionName;
|
|
38
55
|
} else {
|
|
39
56
|
functionName = await findWrappingFunction(node, ancestors);
|
|
40
57
|
}
|
|
41
58
|
|
|
42
|
-
const properties = await extractProperties(node, source);
|
|
59
|
+
const properties = await extractProperties(node, source, matchedConfig);
|
|
43
60
|
|
|
44
61
|
this.events.push({
|
|
45
62
|
eventName,
|
|
@@ -21,6 +21,7 @@ const EXTRACTION_STRATEGIES = {
|
|
|
21
21
|
googleanalytics: extractGoogleAnalyticsEvent,
|
|
22
22
|
snowplow: extractSnowplowEvent,
|
|
23
23
|
mparticle: extractMparticleEvent,
|
|
24
|
+
custom: extractCustomEvent,
|
|
24
25
|
default: extractDefaultEvent
|
|
25
26
|
};
|
|
26
27
|
|
|
@@ -30,10 +31,14 @@ const EXTRACTION_STRATEGIES = {
|
|
|
30
31
|
* @param {string} source - Analytics provider source
|
|
31
32
|
* @param {Object} checker - TypeScript type checker
|
|
32
33
|
* @param {Object} sourceFile - TypeScript source file
|
|
34
|
+
* @param {Object} customConfig - Custom configuration for custom extraction
|
|
33
35
|
* @returns {EventData} Extracted event data
|
|
34
36
|
*/
|
|
35
|
-
function extractEventData(node, source, checker, sourceFile) {
|
|
37
|
+
function extractEventData(node, source, checker, sourceFile, customConfig) {
|
|
36
38
|
const strategy = EXTRACTION_STRATEGIES[source] || EXTRACTION_STRATEGIES.default;
|
|
39
|
+
if (source === 'custom') {
|
|
40
|
+
return strategy(node, checker, sourceFile, customConfig);
|
|
41
|
+
}
|
|
37
42
|
return strategy(node, checker, sourceFile);
|
|
38
43
|
}
|
|
39
44
|
|
|
@@ -121,6 +126,32 @@ function extractMparticleEvent(node, checker, sourceFile) {
|
|
|
121
126
|
return { eventName, propertiesNode };
|
|
122
127
|
}
|
|
123
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Custom extraction
|
|
131
|
+
* @param {Object} node - CallExpression node
|
|
132
|
+
* @param {Object} checker - TypeScript type checker
|
|
133
|
+
* @param {Object} sourceFile - TypeScript source file
|
|
134
|
+
* @param {Object} customConfig - Custom configuration for custom extraction
|
|
135
|
+
* @returns {EventData}
|
|
136
|
+
*/
|
|
137
|
+
function extractCustomEvent(node, checker, sourceFile, customConfig) {
|
|
138
|
+
const args = node.arguments || [];
|
|
139
|
+
|
|
140
|
+
const eventArg = args[customConfig?.eventIndex ?? 0];
|
|
141
|
+
const propertiesArg = args[customConfig?.propertiesIndex ?? 1];
|
|
142
|
+
|
|
143
|
+
const eventName = getStringValue(eventArg, checker, sourceFile);
|
|
144
|
+
|
|
145
|
+
const extraArgs = {};
|
|
146
|
+
if (customConfig && customConfig.extraParams) {
|
|
147
|
+
customConfig.extraParams.forEach(extra => {
|
|
148
|
+
extraArgs[extra.name] = args[extra.idx];
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return { eventName, propertiesNode: propertiesArg, extraArgs };
|
|
153
|
+
}
|
|
154
|
+
|
|
124
155
|
/**
|
|
125
156
|
* Default event extraction for standard providers
|
|
126
157
|
* @param {Object} node - CallExpression node
|
|
@@ -149,9 +180,10 @@ function extractDefaultEvent(node, checker, sourceFile) {
|
|
|
149
180
|
* @param {string} functionName - Containing function name
|
|
150
181
|
* @param {Object} checker - TypeScript type checker
|
|
151
182
|
* @param {Object} sourceFile - TypeScript source file
|
|
183
|
+
* @param {Object} customConfig - Custom configuration for custom extraction
|
|
152
184
|
* @returns {Object|null} Processed event object or null
|
|
153
185
|
*/
|
|
154
|
-
function processEventData(eventData, source, filePath, line, functionName, checker, sourceFile) {
|
|
186
|
+
function processEventData(eventData, source, filePath, line, functionName, checker, sourceFile, customConfig) {
|
|
155
187
|
const { eventName, propertiesNode } = eventData;
|
|
156
188
|
|
|
157
189
|
if (!eventName || !propertiesNode) {
|
|
@@ -184,6 +216,37 @@ function processEventData(eventData, source, filePath, line, functionName, check
|
|
|
184
216
|
// Clean up any unresolved type markers
|
|
185
217
|
const cleanedProperties = cleanupProperties(properties);
|
|
186
218
|
|
|
219
|
+
// Handle custom extra params
|
|
220
|
+
if (source === 'custom' && customConfig && eventData.extraArgs) {
|
|
221
|
+
for (const [paramName, argNode] of Object.entries(eventData.extraArgs)) {
|
|
222
|
+
if (argNode && ts.isObjectLiteralExpression(argNode)) {
|
|
223
|
+
// Extract detailed properties from object literal expression
|
|
224
|
+
cleanedProperties[paramName] = {
|
|
225
|
+
type: 'object',
|
|
226
|
+
properties: extractProperties(checker, argNode)
|
|
227
|
+
};
|
|
228
|
+
} else if (argNode && ts.isIdentifier(argNode)) {
|
|
229
|
+
// Handle identifier references to objects
|
|
230
|
+
const resolvedNode = resolveIdentifierToInitializer(checker, argNode, sourceFile);
|
|
231
|
+
if (resolvedNode && ts.isObjectLiteralExpression(resolvedNode)) {
|
|
232
|
+
cleanedProperties[paramName] = {
|
|
233
|
+
type: 'object',
|
|
234
|
+
properties: extractProperties(checker, resolvedNode)
|
|
235
|
+
};
|
|
236
|
+
} else {
|
|
237
|
+
cleanedProperties[paramName] = {
|
|
238
|
+
type: inferNodeValueType(argNode)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
// For non-object arguments, use simple type inference
|
|
243
|
+
cleanedProperties[paramName] = {
|
|
244
|
+
type: inferNodeValueType(argNode)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
187
250
|
return {
|
|
188
251
|
eventName,
|
|
189
252
|
source,
|
|
@@ -368,6 +431,16 @@ function cleanupProperties(properties) {
|
|
|
368
431
|
return cleaned;
|
|
369
432
|
}
|
|
370
433
|
|
|
434
|
+
function inferNodeValueType(node) {
|
|
435
|
+
if (!node) return 'any';
|
|
436
|
+
if (ts.isStringLiteral(node)) return 'string';
|
|
437
|
+
if (ts.isNumericLiteral(node)) return 'number';
|
|
438
|
+
if (node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword) return 'boolean';
|
|
439
|
+
if (ts.isArrayLiteralExpression(node)) return 'array';
|
|
440
|
+
if (ts.isObjectLiteralExpression(node)) return 'object';
|
|
441
|
+
return 'any';
|
|
442
|
+
}
|
|
443
|
+
|
|
371
444
|
module.exports = {
|
|
372
445
|
extractEventData,
|
|
373
446
|
processEventData
|
|
@@ -9,16 +9,14 @@ const { getProgram, findTrackingEvents, ProgramError, SourceFileError } = requir
|
|
|
9
9
|
* Analyzes a TypeScript file for analytics tracking calls
|
|
10
10
|
* @param {string} filePath - Path to the TypeScript file to analyze
|
|
11
11
|
* @param {Object} [program] - Optional existing TypeScript program to reuse
|
|
12
|
-
* @param {string} [
|
|
12
|
+
* @param {string} [customFunctionSignature] - Optional custom function signature to detect
|
|
13
13
|
* @returns {Array<Object>} Array of tracking events found in the file
|
|
14
14
|
*/
|
|
15
|
-
function analyzeTsFile(filePath, program,
|
|
16
|
-
const events = [];
|
|
17
|
-
|
|
15
|
+
function analyzeTsFile(filePath, program = null, customFunctionSignatures = null) {
|
|
18
16
|
try {
|
|
19
|
-
// Get or create TypeScript program
|
|
17
|
+
// Get or create TypeScript program (only once)
|
|
20
18
|
const tsProgram = getProgram(filePath, program);
|
|
21
|
-
|
|
19
|
+
|
|
22
20
|
// Get source file from program
|
|
23
21
|
const sourceFile = tsProgram.getSourceFile(filePath);
|
|
24
22
|
if (!sourceFile) {
|
|
@@ -28,9 +26,17 @@ function analyzeTsFile(filePath, program, customFunction) {
|
|
|
28
26
|
// Get type checker
|
|
29
27
|
const checker = tsProgram.getTypeChecker();
|
|
30
28
|
|
|
31
|
-
//
|
|
32
|
-
const
|
|
33
|
-
|
|
29
|
+
// Single-pass collection covering built-in + all custom configs
|
|
30
|
+
const events = findTrackingEvents(sourceFile, checker, filePath, customFunctionSignatures || []);
|
|
31
|
+
|
|
32
|
+
// Deduplicate events
|
|
33
|
+
const unique = new Map();
|
|
34
|
+
for (const evt of events) {
|
|
35
|
+
const key = `${evt.source}|${evt.eventName}|${evt.line}|${evt.functionName}`;
|
|
36
|
+
if (!unique.has(key)) unique.set(key, evt);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return Array.from(unique.values());
|
|
34
40
|
|
|
35
41
|
} catch (error) {
|
|
36
42
|
if (error instanceof ProgramError) {
|
|
@@ -42,7 +48,7 @@ function analyzeTsFile(filePath, program, customFunction) {
|
|
|
42
48
|
}
|
|
43
49
|
}
|
|
44
50
|
|
|
45
|
-
return
|
|
51
|
+
return [];
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
module.exports = { analyzeTsFile };
|
|
@@ -65,32 +65,55 @@ function getProgram(filePath, existingProgram) {
|
|
|
65
65
|
* @param {Object} sourceFile - TypeScript source file
|
|
66
66
|
* @param {Object} checker - TypeScript type checker
|
|
67
67
|
* @param {string} filePath - Path to the file being analyzed
|
|
68
|
-
* @param {
|
|
68
|
+
* @param {Array<Object>} [customConfigs] - Array of custom function configurations
|
|
69
69
|
* @returns {Array<Object>} Array of found events
|
|
70
70
|
*/
|
|
71
|
-
function findTrackingEvents(sourceFile, checker, filePath,
|
|
71
|
+
function findTrackingEvents(sourceFile, checker, filePath, customConfigs = []) {
|
|
72
72
|
const events = [];
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
75
|
+
* Helper to test if a CallExpression matches a custom function name.
|
|
76
|
+
* We simply rely on node.expression.getText() which preserves the fully qualified name.
|
|
77
77
|
*/
|
|
78
|
+
const matchesCustomFn = (callNode, fnName) => {
|
|
79
|
+
if (!fnName) return false;
|
|
80
|
+
try {
|
|
81
|
+
return callNode.expression && callNode.expression.getText() === fnName;
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
78
87
|
function visit(node) {
|
|
79
88
|
try {
|
|
80
89
|
if (ts.isCallExpression(node)) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
90
|
+
let matchedCustom = null;
|
|
91
|
+
|
|
92
|
+
if (Array.isArray(customConfigs) && customConfigs.length > 0) {
|
|
93
|
+
for (const cfg of customConfigs) {
|
|
94
|
+
if (cfg && matchesCustomFn(node, cfg.functionName)) {
|
|
95
|
+
matchedCustom = cfg;
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
84
99
|
}
|
|
100
|
+
|
|
101
|
+
const event = extractTrackingEvent(
|
|
102
|
+
node,
|
|
103
|
+
sourceFile,
|
|
104
|
+
checker,
|
|
105
|
+
filePath,
|
|
106
|
+
matchedCustom /* may be null */
|
|
107
|
+
);
|
|
108
|
+
if (event) events.push(event);
|
|
85
109
|
}
|
|
86
|
-
|
|
110
|
+
|
|
87
111
|
ts.forEachChild(node, visit);
|
|
88
112
|
} catch (error) {
|
|
89
113
|
console.error(`Error processing node in ${filePath}:`, error.message);
|
|
90
114
|
}
|
|
91
115
|
}
|
|
92
116
|
|
|
93
|
-
// Start traversal from the root
|
|
94
117
|
ts.forEachChild(sourceFile, visit);
|
|
95
118
|
|
|
96
119
|
return events;
|
|
@@ -102,25 +125,25 @@ function findTrackingEvents(sourceFile, checker, filePath, customFunction) {
|
|
|
102
125
|
* @param {Object} sourceFile - TypeScript source file
|
|
103
126
|
* @param {Object} checker - TypeScript type checker
|
|
104
127
|
* @param {string} filePath - File path
|
|
105
|
-
* @param {
|
|
128
|
+
* @param {Object} [customConfig] - Custom function configuration
|
|
106
129
|
* @returns {Object|null} Extracted event or null
|
|
107
130
|
*/
|
|
108
|
-
function extractTrackingEvent(node, sourceFile, checker, filePath,
|
|
131
|
+
function extractTrackingEvent(node, sourceFile, checker, filePath, customConfig) {
|
|
109
132
|
// Detect the analytics source
|
|
110
|
-
const source = detectAnalyticsSource(node,
|
|
133
|
+
const source = detectAnalyticsSource(node, customConfig?.functionName);
|
|
111
134
|
if (source === 'unknown') {
|
|
112
135
|
return null;
|
|
113
136
|
}
|
|
114
137
|
|
|
115
138
|
// Extract event data based on the source
|
|
116
|
-
const eventData = extractEventData(node, source, checker, sourceFile);
|
|
139
|
+
const eventData = extractEventData(node, source, checker, sourceFile, customConfig);
|
|
117
140
|
|
|
118
141
|
// Get location and context information
|
|
119
142
|
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
120
143
|
const functionName = findWrappingFunction(node);
|
|
121
144
|
|
|
122
145
|
// Process the event data into final format
|
|
123
|
-
return processEventData(eventData, source, filePath, line, functionName, checker, sourceFile);
|
|
146
|
+
return processEventData(eventData, source, filePath, line, functionName, checker, sourceFile, customConfig);
|
|
124
147
|
}
|
|
125
148
|
|
|
126
149
|
module.exports = {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Create new file with parser implementation
|
|
2
|
+
function parseCustomFunctionSignature(signature) {
|
|
3
|
+
if (!signature || typeof signature !== 'string') {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Match function name and optional parameter list
|
|
8
|
+
// Supports names with module prefix like Module.func
|
|
9
|
+
const match = signature.match(/^\s*([A-Za-z0-9_.]+)\s*(?:\(([^)]*)\))?\s*$/);
|
|
10
|
+
if (!match) {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const functionName = match[1].trim();
|
|
15
|
+
const paramsPart = match[2];
|
|
16
|
+
|
|
17
|
+
// Default legacy behaviour: EVENT_NAME, PROPERTIES
|
|
18
|
+
if (!paramsPart) {
|
|
19
|
+
return {
|
|
20
|
+
functionName,
|
|
21
|
+
eventIndex: 0,
|
|
22
|
+
propertiesIndex: 1,
|
|
23
|
+
extraParams: []
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Split params by comma, trimming whitespace
|
|
28
|
+
const params = paramsPart.split(',').map(p => p.trim()).filter(Boolean);
|
|
29
|
+
|
|
30
|
+
const eventIndex = params.findIndex(p => p.toUpperCase() === 'EVENT_NAME');
|
|
31
|
+
let propertiesIndex = params.findIndex(p => p.toUpperCase() === 'PROPERTIES');
|
|
32
|
+
|
|
33
|
+
if (eventIndex === -1) {
|
|
34
|
+
throw new Error('EVENT_NAME is required in custom function signature');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (propertiesIndex === -1) {
|
|
38
|
+
// If PROPERTIES is missing, assume it's at the end of the parameters
|
|
39
|
+
propertiesIndex = params.length;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const extraParams = params.map((name, idx) => ({ idx, name }))
|
|
43
|
+
.filter(p => !(p.idx === eventIndex || p.idx === propertiesIndex));
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
functionName,
|
|
47
|
+
eventIndex,
|
|
48
|
+
propertiesIndex,
|
|
49
|
+
extraParams
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
parseCustomFunctionSignature
|
|
55
|
+
};
|
package/src/index.js
CHANGED
|
@@ -11,8 +11,8 @@ const { generateDescriptions } = require('./generateDescriptions');
|
|
|
11
11
|
const { ChatOpenAI } = require('@langchain/openai');
|
|
12
12
|
const { ChatVertexAI } = require('@langchain/google-vertexai');
|
|
13
13
|
|
|
14
|
-
async function run(targetDir, outputPath,
|
|
15
|
-
let events = await analyzeDirectory(targetDir,
|
|
14
|
+
async function run(targetDir, outputPath, customFunctions, customSourceDetails, generateDescription, provider, model, stdout, format) {
|
|
15
|
+
let events = await analyzeDirectory(targetDir, customFunctions);
|
|
16
16
|
if (generateDescription) {
|
|
17
17
|
let llm;
|
|
18
18
|
if (provider === 'openai') {
|