UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

476 lines (475 loc) 17.3 kB
"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;