@flisk/analyze-tracking 0.7.2 → 0.7.4
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 +4 -0
- package/bin/cli.js +30 -2
- package/package.json +12 -8
- package/src/analyze/go/astTraversal.js +121 -0
- package/src/analyze/go/constants.js +20 -0
- package/src/analyze/go/eventDeduplicator.js +47 -0
- package/src/analyze/go/eventExtractor.js +156 -0
- package/src/analyze/go/goAstParser/constants.js +39 -0
- package/src/analyze/go/goAstParser/expressionParser.js +281 -0
- package/src/analyze/go/goAstParser/index.js +52 -0
- package/src/analyze/go/goAstParser/statementParser.js +387 -0
- package/src/analyze/go/goAstParser/tokenizer.js +196 -0
- package/src/analyze/go/goAstParser/typeParser.js +202 -0
- package/src/analyze/go/goAstParser/utils.js +99 -0
- package/src/analyze/go/index.js +55 -0
- package/src/analyze/go/propertyExtractor.js +670 -0
- package/src/analyze/go/trackingDetector.js +71 -0
- package/src/analyze/go/trackingExtractor.js +54 -0
- package/src/analyze/go/typeContext.js +88 -0
- package/src/analyze/go/utils.js +215 -0
- package/src/analyze/index.js +11 -6
- package/src/analyze/javascript/constants.js +115 -0
- package/src/analyze/javascript/detectors/analytics-source.js +119 -0
- package/src/analyze/javascript/detectors/index.js +10 -0
- package/src/analyze/javascript/extractors/event-extractor.js +179 -0
- package/src/analyze/javascript/extractors/index.js +13 -0
- package/src/analyze/javascript/extractors/property-extractor.js +172 -0
- package/src/analyze/javascript/index.js +38 -0
- package/src/analyze/javascript/parser.js +126 -0
- package/src/analyze/javascript/utils/function-finder.js +123 -0
- package/src/analyze/python/index.js +111 -0
- package/src/analyze/python/pythonTrackingAnalyzer.py +814 -0
- package/src/analyze/ruby/detectors.js +46 -0
- package/src/analyze/ruby/extractors.js +258 -0
- package/src/analyze/ruby/index.js +51 -0
- package/src/analyze/ruby/traversal.js +123 -0
- package/src/analyze/ruby/types.js +30 -0
- package/src/analyze/ruby/visitor.js +66 -0
- package/src/analyze/typescript/constants.js +109 -0
- package/src/analyze/typescript/detectors/analytics-source.js +125 -0
- package/src/analyze/typescript/detectors/index.js +10 -0
- package/src/analyze/typescript/extractors/event-extractor.js +269 -0
- package/src/analyze/typescript/extractors/index.js +14 -0
- package/src/analyze/typescript/extractors/property-extractor.js +427 -0
- package/src/analyze/typescript/index.js +48 -0
- package/src/analyze/typescript/parser.js +131 -0
- package/src/analyze/typescript/utils/function-finder.js +139 -0
- package/src/analyze/typescript/utils/type-resolver.js +208 -0
- package/src/generateDescriptions/index.js +81 -0
- package/src/generateDescriptions/llmUtils.js +33 -0
- package/src/generateDescriptions/promptUtils.js +62 -0
- package/src/generateDescriptions/schemaUtils.js +61 -0
- package/src/index.js +13 -4
- package/src/{fileProcessor.js → utils/fileProcessor.js} +5 -0
- package/src/{repoDetails.js → utils/repoDetails.js} +5 -0
- package/src/utils/yamlGenerator.js +47 -0
- package/src/analyze/analyzeGoFile.js +0 -1164
- package/src/analyze/analyzeJsFile.js +0 -87
- package/src/analyze/analyzePythonFile.js +0 -42
- package/src/analyze/analyzeRubyFile.js +0 -419
- package/src/analyze/analyzeTsFile.js +0 -192
- package/src/analyze/go2json.js +0 -1069
- package/src/analyze/helpers.js +0 -656
- package/src/analyze/pythonTrackingAnalyzer.py +0 -541
- package/src/generateDescriptions.js +0 -196
- package/src/yamlGenerator.js +0 -23
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
const ts = require('typescript');
|
|
2
|
-
const { detectSourceTs, findWrappingFunctionTs, extractTsProperties, resolveIdentifierToInitializer, extractInterfaceProperties } = require('./helpers');
|
|
3
|
-
|
|
4
|
-
function resolveUnresolvedTypes(properties, checker, sourceFile) {
|
|
5
|
-
const resolved = {};
|
|
6
|
-
|
|
7
|
-
for (const [key, value] of Object.entries(properties)) {
|
|
8
|
-
if (value && typeof value === 'object') {
|
|
9
|
-
if (value.__unresolved) {
|
|
10
|
-
// Try to find and resolve the type
|
|
11
|
-
const typeName = value.__unresolved;
|
|
12
|
-
delete value.__unresolved;
|
|
13
|
-
|
|
14
|
-
// This is a simplified approach - in practice, you'd need to find the actual type declaration
|
|
15
|
-
// For now, we'll keep the object type but remove the unresolved marker
|
|
16
|
-
resolved[key] = value;
|
|
17
|
-
} else if (value.type === 'array' && value.items && value.items.__unresolved) {
|
|
18
|
-
// Handle unresolved array element types
|
|
19
|
-
const itemTypeName = value.items.__unresolved;
|
|
20
|
-
delete value.items.__unresolved;
|
|
21
|
-
resolved[key] = value;
|
|
22
|
-
} else if (value.type === 'array' && value.items && typeof value.items.type === 'string' && value.items.type.includes(' ')) {
|
|
23
|
-
// Handle types like "readonly Product" - extract the actual type name
|
|
24
|
-
const typeString = value.items.type;
|
|
25
|
-
const actualType = typeString.replace(/^readonly\s+/, '').trim();
|
|
26
|
-
|
|
27
|
-
// If it looks like a custom type, mark it as object
|
|
28
|
-
if (actualType[0] === actualType[0].toUpperCase() && !actualType.includes('<')) {
|
|
29
|
-
resolved[key] = {
|
|
30
|
-
...value,
|
|
31
|
-
items: {
|
|
32
|
-
type: 'object'
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
} else {
|
|
36
|
-
resolved[key] = value;
|
|
37
|
-
}
|
|
38
|
-
} else if (value.type === 'object' && value.properties) {
|
|
39
|
-
// Recursively resolve nested properties
|
|
40
|
-
resolved[key] = {
|
|
41
|
-
...value,
|
|
42
|
-
properties: resolveUnresolvedTypes(value.properties, checker, sourceFile)
|
|
43
|
-
};
|
|
44
|
-
} else if (value.type === 'array' && value.items && value.items.properties) {
|
|
45
|
-
// Recursively resolve array item properties
|
|
46
|
-
resolved[key] = {
|
|
47
|
-
...value,
|
|
48
|
-
items: {
|
|
49
|
-
...value.items,
|
|
50
|
-
properties: value.items.properties ? resolveUnresolvedTypes(value.items.properties, checker, sourceFile) : undefined
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
} else {
|
|
54
|
-
resolved[key] = value;
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
resolved[key] = value;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return resolved;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function analyzeTsFile(filePath, program, customFunction) {
|
|
65
|
-
let events = [];
|
|
66
|
-
try {
|
|
67
|
-
const sourceFile = program.getSourceFile(filePath);
|
|
68
|
-
if (!sourceFile) {
|
|
69
|
-
console.error(`Error: Unable to get source file for ${filePath}`);
|
|
70
|
-
return events;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const checker = program.getTypeChecker();
|
|
74
|
-
|
|
75
|
-
function visit(node) {
|
|
76
|
-
try {
|
|
77
|
-
if (ts.isCallExpression(node)) {
|
|
78
|
-
const source = detectSourceTs(node, customFunction);
|
|
79
|
-
if (source === 'unknown') return;
|
|
80
|
-
|
|
81
|
-
let eventName = null;
|
|
82
|
-
let propertiesNode = null;
|
|
83
|
-
|
|
84
|
-
if (source === 'googleanalytics' && node.arguments.length >= 3) {
|
|
85
|
-
eventName = node.arguments[1]?.text || null;
|
|
86
|
-
propertiesNode = node.arguments[2];
|
|
87
|
-
} else if (source === 'snowplow' && node.arguments.length > 0) {
|
|
88
|
-
// Snowplow pattern: tracker.track(buildStructEvent({...})) or tracker.track(payload)
|
|
89
|
-
const firstArg = node.arguments[0];
|
|
90
|
-
|
|
91
|
-
// Check if it's a direct buildStructEvent call
|
|
92
|
-
if (ts.isCallExpression(firstArg) &&
|
|
93
|
-
ts.isIdentifier(firstArg.expression) &&
|
|
94
|
-
firstArg.expression.escapedText === 'buildStructEvent' &&
|
|
95
|
-
firstArg.arguments.length > 0) {
|
|
96
|
-
const structEventArg = firstArg.arguments[0];
|
|
97
|
-
if (ts.isObjectLiteralExpression(structEventArg)) {
|
|
98
|
-
// Find the action property for event name
|
|
99
|
-
const actionProp = structEventArg.properties.find(
|
|
100
|
-
prop => prop.name && prop.name.escapedText === 'action'
|
|
101
|
-
);
|
|
102
|
-
if (actionProp && actionProp.initializer && ts.isStringLiteral(actionProp.initializer)) {
|
|
103
|
-
eventName = actionProp.initializer.text;
|
|
104
|
-
propertiesNode = structEventArg;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
// Check if it's a variable reference (e.g., const payload = buildStructEvent({...}))
|
|
109
|
-
else if (ts.isIdentifier(firstArg)) {
|
|
110
|
-
const resolvedNode = resolveIdentifierToInitializer(checker, firstArg, sourceFile);
|
|
111
|
-
if (resolvedNode && ts.isCallExpression(resolvedNode) &&
|
|
112
|
-
ts.isIdentifier(resolvedNode.expression) &&
|
|
113
|
-
resolvedNode.expression.escapedText === 'buildStructEvent' &&
|
|
114
|
-
resolvedNode.arguments.length > 0) {
|
|
115
|
-
const structEventArg = resolvedNode.arguments[0];
|
|
116
|
-
if (ts.isObjectLiteralExpression(structEventArg)) {
|
|
117
|
-
const actionProp = structEventArg.properties.find(
|
|
118
|
-
prop => prop.name && prop.name.escapedText === 'action'
|
|
119
|
-
);
|
|
120
|
-
if (actionProp && actionProp.initializer && ts.isStringLiteral(actionProp.initializer)) {
|
|
121
|
-
eventName = actionProp.initializer.text;
|
|
122
|
-
propertiesNode = structEventArg;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
} else if (source === 'mparticle' && node.arguments.length >= 3) {
|
|
128
|
-
// mParticle: first param is event name, second is event type (ignored), third is properties
|
|
129
|
-
eventName = node.arguments[0]?.text || null;
|
|
130
|
-
propertiesNode = node.arguments[2];
|
|
131
|
-
} else if (node.arguments.length >= 2) {
|
|
132
|
-
eventName = node.arguments[0]?.text || null;
|
|
133
|
-
propertiesNode = node.arguments[1];
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
137
|
-
const functionName = findWrappingFunctionTs(node);
|
|
138
|
-
|
|
139
|
-
if (eventName && propertiesNode) {
|
|
140
|
-
try {
|
|
141
|
-
let properties = null;
|
|
142
|
-
|
|
143
|
-
// Check if properties is an object literal
|
|
144
|
-
if (ts.isObjectLiteralExpression(propertiesNode)) {
|
|
145
|
-
properties = extractTsProperties(checker, propertiesNode);
|
|
146
|
-
}
|
|
147
|
-
// Check if properties is an identifier (variable reference)
|
|
148
|
-
else if (ts.isIdentifier(propertiesNode)) {
|
|
149
|
-
const resolvedNode = resolveIdentifierToInitializer(checker, propertiesNode, sourceFile);
|
|
150
|
-
if (resolvedNode && ts.isObjectLiteralExpression(resolvedNode)) {
|
|
151
|
-
properties = extractTsProperties(checker, resolvedNode);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (properties) {
|
|
156
|
-
// For Snowplow, remove 'action' from properties since it's used as the event name
|
|
157
|
-
if (source === 'snowplow' && properties.action) {
|
|
158
|
-
delete properties.action;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// Clean up any unresolved type markers
|
|
162
|
-
const cleanedProperties = resolveUnresolvedTypes(properties, checker, sourceFile);
|
|
163
|
-
|
|
164
|
-
events.push({
|
|
165
|
-
eventName,
|
|
166
|
-
source,
|
|
167
|
-
properties: cleanedProperties,
|
|
168
|
-
filePath,
|
|
169
|
-
line,
|
|
170
|
-
functionName
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
} catch (propertyError) {
|
|
174
|
-
console.error(`Error extracting properties in ${filePath} at line ${line}`);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
ts.forEachChild(node, visit);
|
|
179
|
-
} catch (nodeError) {
|
|
180
|
-
console.error(`Error processing node in ${filePath}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
ts.forEachChild(sourceFile, visit);
|
|
185
|
-
} catch (fileError) {
|
|
186
|
-
console.error(`Error analyzing TypeScript file ${filePath}`);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return events;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
module.exports = { analyzeTsFile };
|