From f2bc6c81ae1132b8bbacbf2b24a48fc8e6fbfaf2 Mon Sep 17 00:00:00 2001 From: Sameen Karim Date: Wed, 11 Sep 2024 14:35:23 -0400 Subject: [PATCH] gracefully handle parsing errors for bad files --- package-lock.json | 4 +- package.json | 2 +- src/analyze/analyzeJsFile.js | 96 +++++++++++++++++++++--------------- src/analyze/analyzeTsFile.js | 95 ++++++++++++++++++++--------------- 4 files changed, 114 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f68de5..7d25267 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@flisk/analyze-tracking", - "version": "0.2.8", + "version": "0.2.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@flisk/analyze-tracking", - "version": "0.2.8", + "version": "0.2.9", "license": "MIT", "dependencies": { "@typescript-eslint/parser": "^8.1.0", diff --git a/package.json b/package.json index 4083759..8727dda 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/src/analyze/analyzeJsFile.js b/src/analyze/analyzeJsFile.js index 865d0be..d7de40b 100644 --- a/src/analyze/analyzeJsFile.js +++ b/src/analyze/analyzeJsFile.js @@ -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; } diff --git a/src/analyze/analyzeTsFile.js b/src/analyze/analyzeTsFile.js index 0b79171..c36b5e7 100644 --- a/src/analyze/analyzeTsFile.js +++ b/src/analyze/analyzeTsFile.js @@ -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; }