agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
214 lines (187 loc) • 6.38 kB
JavaScript
/**
* @file Context analyzer for static bug detection
* @description Determines file context (test, config, framework) for appropriate rules
*/
/**
* Check if file is a test file
* @param {string} filePath - File path
* @returns {boolean} True if test file
*/
function isTestFile(filePath) {
const testPatterns = [
'.test.', '.spec.', '__tests__', '__test__',
'test/', 'tests/', 'spec/', 'specs/',
'.test.js', '.spec.js', 'test.js', 'spec.js'
];
const lowerPath = filePath.toLowerCase();
return testPatterns.some(pattern => lowerPath.includes(pattern));
}
/**
* Check if file is a configuration file
* @param {string} filePath - File path
* @returns {boolean} True if config file
*/
function isConfigFile(filePath) {
const configPatterns = [
'config.js', 'configuration.js', '.config.js',
'webpack.', 'rollup.', 'vite.', 'babel.',
'jest.config', 'tsconfig', 'jsconfig',
'package.json', '.eslintrc', '.prettierrc'
];
const filename = filePath.split('/').pop().toLowerCase();
return configPatterns.some(pattern => filename.includes(pattern));
}
/**
* Check if file is a CLI tool
* @param {string} filePath - File path
* @param {string} content - File content
* @returns {boolean} True if CLI tool
*/
function isCLITool(filePath, content) {
let weight = 0;
const lowerPath = filePath.toLowerCase();
// Strong path indicators (high weight)
if (lowerPath.includes('/bin/')) weight += 5;
if (lowerPath.includes('/cli/')) weight += 4;
if (lowerPath.includes('/scripts/')) weight += 3;
// Filename patterns (medium weight)
const filename = lowerPath.split('/').pop();
if (filename === 'cli.js') weight += 4;
if (filename.includes('-cli.js')) weight += 3;
if (filename.startsWith('analyze-')) weight += 3;
if (filename.includes('script')) weight += 2;
if (filename.includes('tool')) weight += 2;
// Shebang is a very strong indicator
if (content.startsWith('#!/usr/bin/env node') ||
content.startsWith('#!/usr/bin/node')) {
weight += 6;
}
// CLI-specific patterns with weights
const cliPatterns = [
{ pattern: 'process.exit', weight: 3 },
{ pattern: 'process.argv', weight: 3 },
{ pattern: 'getProcessArgs', weight: 3 },
{ pattern: 'parseArgs', weight: 3 },
{ pattern: 'showHelp', weight: 2 },
{ pattern: '__dirname', weight: 1 },
{ pattern: 'console.log', weight: 1 },
{ pattern: 'console.error', weight: 1 },
{ pattern: 'require(', weight: 0.5 }
];
// Add weights for each pattern found
cliPatterns.forEach(({ pattern, weight: patternWeight }) => {
if (content.includes(pattern)) {
weight += patternWeight;
}
});
// Check for main function pattern common in CLIs
if (content.includes('function main()') ||
content.includes('async function main()')) {
weight += 2;
}
// Check for commander/yargs/minimist usage
if (content.includes('require("commander")') ||
content.includes('require("yargs")') ||
content.includes('require("minimist")') ||
content.includes("require('commander')") ||
content.includes("require('yargs')") ||
content.includes("require('minimist')")) {
weight += 4;
}
// Lower threshold - if weight >= 5, it's likely a CLI
return weight >= 5;
}
/**
* Detect framework from file content and structure
* @param {string} content - File content
* @param {string} filePath - File path
* @returns {string} Framework name or 'vanilla'
*/
function detectFramework(content, filePath) {
// React detection
if (content.includes('import React') || content.includes('from "react"') ||
content.includes("from 'react'") || content.includes('useState') ||
content.includes('useEffect') || content.includes('jsx')) {
return 'react';
}
// Vue detection
if (content.includes('import Vue') || content.includes('from "vue"') ||
content.includes("from 'vue'") || content.includes('createApp') ||
filePath.endsWith('.vue')) {
return 'vue';
}
// Angular detection
if (content.includes('@angular/') || content.includes('import { Component }') ||
content.includes('@Component') || content.includes('ngOnInit')) {
return 'angular';
}
// Node.js backend detection
if (content.includes('express') || content.includes('require("http")') ||
content.includes('createServer') || content.includes('app.listen')) {
return 'node';
}
return 'vanilla';
}
/**
* Get appropriate bug detection rules based on context
* @param {string} filePath - File path
* @param {string} content - File content
* @returns {Object} Context info with applicable rules
*/
function getFileContext(filePath, content) {
const context = {
isTest: isTestFile(filePath),
isConfig: isConfigFile(filePath),
isCLI: isCLITool(filePath, content),
framework: detectFramework(content, filePath),
rules: {
allowEval: false,
allowConsoleLog: false,
checkAsyncAwait: true,
checkNullSafety: true,
checkResourceLeaks: true,
frameworkSpecific: true
}
};
// Adjust rules based on context
if (context.isTest) {
context.rules.allowEval = true; // Tests might use eval for dynamic scenarios
context.rules.allowConsoleLog = true; // Tests often log for debugging
}
if (context.isConfig) {
context.rules.checkAsyncAwait = false; // Config files rarely use async
context.rules.checkResourceLeaks = false; // Not applicable to configs
}
if (context.isCLI) {
context.rules.allowConsoleLog = true; // CLI tools need console.log for output
}
return context;
}
/**
* Check if a pattern should be ignored based on context
* @param {string} bugType - Type of bug
* @param {Object} context - File context
* @returns {boolean} True if should be ignored
*/
function shouldIgnoreBug(bugType, context) {
if (context.isTest) {
// Ignore certain patterns in test files
if (['dangerous_eval', 'console_log', 'hardcoded_values'].includes(bugType)) {
return true;
}
}
if (context.isConfig) {
// Ignore certain patterns in config files
if (['unused_variable', 'missing_await'].includes(bugType)) {
return true;
}
}
return false;
}
module.exports = {
isTestFile,
isConfigFile,
detectFramework,
getFileContext,
shouldIgnoreBug
};