Skip to content

Commit

Permalink
gracefully handle parsing errors for bad files
Browse files Browse the repository at this point in the history
  • Loading branch information
skarim committed Sep 11, 2024
1 parent 890e53d commit f2bc6c8
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 83 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@flisk/analyze-tracking",
"version": "0.2.8",
"version": "0.2.9",
"description": "Analyzes tracking code in a project and generates data schemas",
"main": "src/index.js",
"bin": {
Expand Down
96 changes: 55 additions & 41 deletions src/analyze/analyzeJsFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,61 @@ const parserOptions = { ecmaVersion: 'latest', sourceType: 'module', locations:
extend(walk.base);

function analyzeJsFile(filePath, customFunction) {
const code = fs.readFileSync(filePath, 'utf8');
const ast = parser.parse(code, parserOptions);
const events = [];

walk.ancestor(ast, {
CallExpression(node, ancestors) {
const source = detectSourceJs(node, customFunction);
if (source === 'unknown') return;

let eventName = null;
let propertiesNode = null;

if (source === 'googleanalytics' && node.arguments.length >= 3) {
eventName = node.arguments[1]?.value || null;
propertiesNode = node.arguments[2];
} else if (source === 'snowplow' && node.arguments.length >= 2) {
const actionProperty = node.arguments[1].properties.find(prop => prop.key.name === 'action');
eventName = actionProperty ? actionProperty.value.value : null;
propertiesNode = node.arguments[1];
} else if (node.arguments.length >= 2) {
eventName = node.arguments[0]?.value || null;
propertiesNode = node.arguments[1];
}

const line = node.loc.start.line;
const functionName = findWrappingFunctionJs(node, ancestors);

if (eventName && propertiesNode && propertiesNode.type === 'ObjectExpression') {
const properties = extractJsProperties(propertiesNode);

events.push({
eventName,
source,
properties,
filePath,
line,
functionName
});
}
},
});
let events = [];
try {
const code = fs.readFileSync(filePath, 'utf8');
let ast;
try {
ast = parser.parse(code, parserOptions);
} catch (parseError) {
console.error(`Error parsing file ${filePath}`);
return events; // Return empty events array if parsing fails
}

walk.ancestor(ast, {
CallExpression(node, ancestors) {
try {
const source = detectSourceJs(node, customFunction);
if (source === 'unknown') return;

let eventName = null;
let propertiesNode = null;

if (source === 'googleanalytics' && node.arguments.length >= 3) {
eventName = node.arguments[1]?.value || null;
propertiesNode = node.arguments[2];
} else if (source === 'snowplow' && node.arguments.length >= 2) {
const actionProperty = node.arguments[1].properties.find(prop => prop.key.name === 'action');
eventName = actionProperty ? actionProperty.value.value : null;
propertiesNode = node.arguments[1];
} else if (node.arguments.length >= 2) {
eventName = node.arguments[0]?.value || null;
propertiesNode = node.arguments[1];
}

const line = node.loc.start.line;
const functionName = findWrappingFunctionJs(node, ancestors);

if (eventName && propertiesNode && propertiesNode.type === 'ObjectExpression') {
const properties = extractJsProperties(propertiesNode);

events.push({
eventName,
source,
properties,
filePath,
line,
functionName
});
}
} catch (nodeError) {
console.error(`Error processing node in ${filePath}`);
}
},
});
} catch (fileError) {
console.error(`Error reading or processing file ${filePath}`);
}

return events;
}
Expand Down
95 changes: 56 additions & 39 deletions src/analyze/analyzeTsFile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,66 @@ const ts = require('typescript');
const { detectSourceTs, findWrappingFunctionTs, extractTsProperties } = require('./helpers');

function analyzeTsFile(filePath, program, customFunction) {
const sourceFile = program.getSourceFile(filePath);
const checker = program.getTypeChecker();
const events = [];

function visit(node) {
if (ts.isCallExpression(node)) {
const source = detectSourceTs(node, customFunction);
if (source === 'unknown') return;

let eventName = null;
let propertiesNode = null;

if (source === 'googleanalytics' && node.arguments.length >= 3) {
eventName = node.arguments[1]?.text || null;
propertiesNode = node.arguments[2];
} else if (source === 'snowplow' && node.arguments.length >= 2) {
const actionProperty = node.arguments[1].properties.find(prop => prop.name.escapedText === 'action');
eventName = actionProperty ? actionProperty.initializer.text : null;
propertiesNode = node.arguments[1];
} else if (node.arguments.length >= 2) {
eventName = node.arguments[0]?.text || null;
propertiesNode = node.arguments[1];
}
let events = [];
try {
const sourceFile = program.getSourceFile(filePath);
if (!sourceFile) {
console.error(`Error: Unable to get source file for ${filePath}`);
return events;
}

const checker = program.getTypeChecker();

function visit(node) {
try {
if (ts.isCallExpression(node)) {
const source = detectSourceTs(node, customFunction);
if (source === 'unknown') return;

let eventName = null;
let propertiesNode = null;

const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
const functionName = findWrappingFunctionTs(node);

if (eventName && propertiesNode && ts.isObjectLiteralExpression(propertiesNode)) {
const properties = extractTsProperties(checker, propertiesNode);
events.push({
eventName,
source,
properties,
filePath,
line,
functionName
});
if (source === 'googleanalytics' && node.arguments.length >= 3) {
eventName = node.arguments[1]?.text || null;
propertiesNode = node.arguments[2];
} else if (source === 'snowplow' && node.arguments.length >= 2) {
const actionProperty = node.arguments[1].properties.find(prop => prop.name.escapedText === 'action');
eventName = actionProperty ? actionProperty.initializer.text : null;
propertiesNode = node.arguments[1];
} else if (node.arguments.length >= 2) {
eventName = node.arguments[0]?.text || null;
propertiesNode = node.arguments[1];
}

const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
const functionName = findWrappingFunctionTs(node);

if (eventName && propertiesNode && ts.isObjectLiteralExpression(propertiesNode)) {
try {
const properties = extractTsProperties(checker, propertiesNode);
events.push({
eventName,
source,
properties,
filePath,
line,
functionName
});
} catch (propertyError) {
console.error(`Error extracting properties in ${filePath} at line ${line}`);
}
}
}
ts.forEachChild(node, visit);
} catch (nodeError) {
console.error(`Error processing node in ${filePath}`);
}
}
ts.forEachChild(node, visit);
}

ts.forEachChild(sourceFile, visit);
ts.forEachChild(sourceFile, visit);
} catch (fileError) {
console.error(`Error analyzing TypeScript file ${filePath}`);
}

return events;
}
Expand Down

0 comments on commit f2bc6c8

Please sign in to comment.