sicua
Version:
A tool for analyzing project structure and dependencies
476 lines (475 loc) • 17.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FunctionFilter = void 0;
const functionFilter_1 = require("../../../utils/common/functionFilter");
const errorHandler_1 = require("../utils/errorHandler");
/**
* Extended filter class for determining which functions to include in analysis
* Wraps the common FunctionFilter class and adds analyzer-specific extensions
*/
class FunctionFilter {
constructor(errorHandler, config = {}) {
this.commonFilter = new functionFilter_1.FunctionFilter();
this.errorHandler = errorHandler || new errorHandler_1.ErrorHandler({ logErrors: false });
this.config = {
enableCommonFilter: true,
enableFrameworkSpecificFilters: true,
enableCustomFilters: true,
enableNodeSizeFilters: true,
minFunctionSize: 10,
maxFunctionSize: 10000,
excludeTestFiles: true,
excludeNodeModules: true,
customExcludePatterns: [],
customIncludePatterns: [],
...config,
};
}
/**
* Enhanced function inclusion check with detailed filtering
* @param node The function AST node
* @param functionName The name of the function
* @param filePath Optional file path for context
* @returns boolean indicating whether the function should be included
*/
shouldIncludeFunction(node, functionName, filePath) {
const result = this.shouldIncludeFunctionDetailed(node, functionName, filePath);
return result.shouldInclude;
}
/**
* Detailed function inclusion check with reasoning
* @param node The function AST node
* @param functionName The name of the function
* @param filePath Optional file path for context
* @returns Detailed filter result
*/
shouldIncludeFunctionDetailed(node, functionName, filePath) {
const appliedFilters = [];
let confidence = 1.0;
try {
// Quick validation filters
const validationResult = this.applyValidationFilters(node, functionName, filePath);
appliedFilters.push(...validationResult.appliedFilters);
if (!validationResult.shouldInclude) {
return {
shouldInclude: false,
reason: validationResult.reason,
confidence: validationResult.confidence,
appliedFilters,
};
}
// File-level filters
if (filePath) {
const fileResult = this.applyFileFilters(filePath);
appliedFilters.push(...fileResult.appliedFilters);
if (!fileResult.shouldInclude) {
return {
shouldInclude: false,
reason: fileResult.reason,
confidence: fileResult.confidence,
appliedFilters,
};
}
confidence *= fileResult.confidence;
}
// Common filter (existing logic)
if (this.config.enableCommonFilter) {
const commonResult = this.applyCommonFilter(node, functionName);
appliedFilters.push(...commonResult.appliedFilters);
if (!commonResult.shouldInclude) {
return {
shouldInclude: false,
reason: commonResult.reason,
confidence: commonResult.confidence,
appliedFilters,
};
}
confidence *= commonResult.confidence;
}
// Framework-specific filters
if (this.config.enableFrameworkSpecificFilters) {
const frameworkResult = this.applyFrameworkFilters(node, functionName);
appliedFilters.push(...frameworkResult.appliedFilters);
if (!frameworkResult.shouldInclude) {
return {
shouldInclude: false,
reason: frameworkResult.reason,
confidence: frameworkResult.confidence,
appliedFilters,
};
}
confidence *= frameworkResult.confidence;
}
// Node size filters
if (this.config.enableNodeSizeFilters) {
const sizeResult = this.applyNodeSizeFilters(node, functionName);
appliedFilters.push(...sizeResult.appliedFilters);
if (!sizeResult.shouldInclude) {
return {
shouldInclude: false,
reason: sizeResult.reason,
confidence: sizeResult.confidence,
appliedFilters,
};
}
confidence *= sizeResult.confidence;
}
// Custom filters
if (this.config.enableCustomFilters) {
const customResult = this.applyCustomFilters(node, functionName, filePath);
appliedFilters.push(...customResult.appliedFilters);
if (!customResult.shouldInclude) {
return {
shouldInclude: false,
reason: customResult.reason,
confidence: customResult.confidence,
appliedFilters,
};
}
confidence *= customResult.confidence;
}
return {
shouldInclude: true,
reason: "Passed all filters",
confidence,
appliedFilters,
};
}
catch (error) {
this.errorHandler.handleFileError(filePath || "unknown", `function filtering for ${functionName}`, error);
return {
shouldInclude: false,
reason: "Error during filtering",
confidence: 0.0,
appliedFilters: [...appliedFilters, "error_filter"],
};
}
}
/**
* Applies basic validation filters
*/
applyValidationFilters(node, functionName, filePath) {
// Check for valid node
if (!node) {
return {
shouldInclude: false,
reason: "Invalid node",
confidence: 1.0,
appliedFilters: ["null_node_filter"],
};
}
// Check for valid function name
if (!functionName || functionName.trim() === "") {
return {
shouldInclude: false,
reason: "Invalid function name",
confidence: 1.0,
appliedFilters: ["invalid_name_filter"],
};
}
// Check for very obvious exclusions
if (functionName === "Anonymous Function" ||
functionName === "ErrorFunction") {
return {
shouldInclude: false,
reason: "Fallback function name",
confidence: 0.9,
appliedFilters: ["fallback_name_filter"],
};
}
return {
shouldInclude: true,
reason: "Passed validation",
confidence: 1.0,
appliedFilters: ["validation_filter"],
};
}
/**
* Applies file-level filters
*/
applyFileFilters(filePath) {
const appliedFilters = [];
// Test file exclusion
if (this.config.excludeTestFiles && this.isTestFile(filePath)) {
return {
shouldInclude: false,
reason: "Test file excluded",
confidence: 1.0,
appliedFilters: ["test_file_filter"],
};
}
appliedFilters.push("test_file_filter");
// Node modules exclusion
if (this.config.excludeNodeModules && this.isNodeModulesFile(filePath)) {
return {
shouldInclude: false,
reason: "Node modules file excluded",
confidence: 1.0,
appliedFilters: ["node_modules_filter"],
};
}
appliedFilters.push("node_modules_filter");
return {
shouldInclude: true,
reason: "Passed file filters",
confidence: 1.0,
appliedFilters,
};
}
/**
* Applies common filter logic with error handling
*/
applyCommonFilter(node, functionName) {
return this.errorHandler.safeExecute(() => {
const shouldInclude = this.commonFilter.shouldIncludeFunction(node, functionName);
return {
shouldInclude,
reason: shouldInclude
? "Passed common filter"
: "Excluded by common filter",
confidence: 0.9,
appliedFilters: ["common_filter"],
};
}, {
shouldInclude: false,
reason: "Error in common filter",
confidence: 0.0,
appliedFilters: ["common_filter_error"],
}, "common filter application");
}
/**
* Applies framework-specific filters
*/
applyFrameworkFilters(node, functionName) {
const appliedFilters = [];
// Next.js specific filters
if (this.isNextJSApiFunction(node, functionName)) {
return {
shouldInclude: false,
reason: "Next.js API route function",
confidence: 0.95,
appliedFilters: ["nextjs_api_filter"],
};
}
appliedFilters.push("nextjs_api_filter");
// Next.js page functions
if (this.isNextJSPageFunction(node, functionName)) {
return {
shouldInclude: false,
reason: "Next.js page function",
confidence: 0.9,
appliedFilters: ["nextjs_page_filter"],
};
}
appliedFilters.push("nextjs_page_filter");
// Jest/testing framework functions
if (this.isTestFrameworkFunction(node, functionName)) {
return {
shouldInclude: false,
reason: "Test framework function",
confidence: 0.95,
appliedFilters: ["test_framework_filter"],
};
}
appliedFilters.push("test_framework_filter");
// Storybook functions
if (this.isStorybookFunction(node, functionName)) {
return {
shouldInclude: false,
reason: "Storybook function",
confidence: 0.9,
appliedFilters: ["storybook_filter"],
};
}
appliedFilters.push("storybook_filter");
return {
shouldInclude: true,
reason: "Passed framework filters",
confidence: 1.0,
appliedFilters,
};
}
/**
* Applies node size filters
*/
applyNodeSizeFilters(node, functionName) {
const nodeText = node.getText();
const nodeSize = nodeText.length;
if (nodeSize < this.config.minFunctionSize) {
return {
shouldInclude: false,
reason: `Function too small (${nodeSize} chars, min: ${this.config.minFunctionSize})`,
confidence: 0.8,
appliedFilters: ["min_size_filter"],
};
}
if (nodeSize > this.config.maxFunctionSize) {
return {
shouldInclude: false,
reason: `Function too large (${nodeSize} chars, max: ${this.config.maxFunctionSize})`,
confidence: 0.9,
appliedFilters: ["max_size_filter"],
};
}
return {
shouldInclude: true,
reason: "Passed size filters",
confidence: 1.0,
appliedFilters: ["size_filter"],
};
}
/**
* Applies custom user-defined filters
*/
applyCustomFilters(node, functionName, filePath) {
const appliedFilters = [];
// Custom exclude patterns
for (const pattern of this.config.customExcludePatterns) {
if (this.matchesPattern(functionName, pattern) ||
(filePath && this.matchesPattern(filePath, pattern))) {
return {
shouldInclude: false,
reason: `Matched exclude pattern: ${pattern}`,
confidence: 0.85,
appliedFilters: ["custom_exclude_filter"],
};
}
}
appliedFilters.push("custom_exclude_filter");
// Custom include patterns (if specified, function must match at least one)
if (this.config.customIncludePatterns.length > 0) {
let matchesIncludePattern = false;
for (const pattern of this.config.customIncludePatterns) {
if (this.matchesPattern(functionName, pattern) ||
(filePath && this.matchesPattern(filePath, pattern))) {
matchesIncludePattern = true;
break;
}
}
if (!matchesIncludePattern) {
return {
shouldInclude: false,
reason: "Does not match any include pattern",
confidence: 0.8,
appliedFilters: ["custom_include_filter"],
};
}
}
appliedFilters.push("custom_include_filter");
return {
shouldInclude: true,
reason: "Passed custom filters",
confidence: 1.0,
appliedFilters,
};
}
/**
* Checks if file is a test file
*/
isTestFile(filePath) {
const lowerPath = filePath.toLowerCase();
return (lowerPath.includes(".test.") ||
lowerPath.includes(".spec.") ||
lowerPath.includes("__tests__") ||
lowerPath.includes("/test/") ||
lowerPath.includes("/tests/"));
}
/**
* Checks if file is in node_modules
*/
isNodeModulesFile(filePath) {
return filePath.includes("node_modules");
}
/**
* Checks if function is a Next.js API route function
*/
isNextJSApiFunction(node, functionName) {
// Check for default export in pages/api directory
if (functionName.includes("Default") || functionName === "handler") {
const sourceFile = node.getSourceFile();
const filePath = sourceFile.fileName.toLowerCase();
return (filePath.includes("/pages/api/") || filePath.includes("\\pages\\api\\"));
}
return false;
}
/**
* Checks if function is a Next.js page function
*/
isNextJSPageFunction(node, functionName) {
const nextJSPageFunctions = [
"getServerSideProps",
"getStaticProps",
"getStaticPaths",
"getInitialProps",
];
return nextJSPageFunctions.includes(functionName);
}
/**
* Checks if function is a test framework function
*/
isTestFrameworkFunction(node, functionName) {
const testFunctions = [
"describe",
"it",
"test",
"expect",
"beforeEach",
"afterEach",
"beforeAll",
"afterAll",
"jest",
"mock",
"spy",
];
return testFunctions.some((testFn) => functionName.toLowerCase().includes(testFn.toLowerCase()));
}
/**
* Checks if function is a Storybook function
*/
isStorybookFunction(node, functionName) {
return (functionName.toLowerCase().includes("story") ||
functionName.toLowerCase().includes("stories") ||
(functionName === "default" &&
node.getSourceFile().fileName.includes(".stories.")));
}
/**
* Matches a string against a pattern (supports wildcards)
*/
matchesPattern(text, pattern) {
// Convert glob pattern to regex
const regexPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
try {
const regex = new RegExp(`^${regexPattern}$`, "i");
return regex.test(text);
}
catch (error) {
// If pattern is invalid, do literal comparison
return text.toLowerCase().includes(pattern.toLowerCase());
}
}
/**
* Updates filter configuration
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
/**
* Gets current filter configuration
*/
getConfig() {
return { ...this.config };
}
/**
* Gets filter statistics
*/
getFilterStats() {
// This would need to be implemented with usage tracking
// For now, return placeholder
return {
totalFiltersApplied: 0,
filtersByType: {},
averageConfidence: 1.0,
};
}
}
exports.FunctionFilter = FunctionFilter;