UNPKG

sicua

Version:

A tool for analyzing project structure and dependencies

248 lines (247 loc) 9.1 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.NamingUtils = void 0; const path_browserify_1 = __importDefault(require("path-browserify")); /** * Utilities for generating and working with type names */ class NamingUtils { /** * Find the longest common prefix in an array of strings */ static findLongestCommonPrefix(strings) { if (strings.length === 0) return ""; if (strings.length === 1) return strings[0]; let prefix = ""; const firstStr = strings[0]; for (let i = 0; i < firstStr.length; i++) { const char = firstStr[i]; if (strings.every((str) => str[i] === char)) { prefix += char; } else { break; } } return prefix; } /** * Find a suggested name for a unified type based on component names */ static findSuggestedName(typeNames) { // Look for common patterns in the names const words = new Set(); for (const name of typeNames) { // Split the name on camel case and gather words const parts = name.split(/(?=[A-Z])/).filter((p) => p.length > 0); parts.forEach((part) => { if (part.length >= 3) { // Only consider significant words words.add(part.toLowerCase()); } }); } // Count word frequencies const wordCounts = {}; words.forEach((word) => { wordCounts[word] = 0; typeNames.forEach((name) => { if (name.toLowerCase().includes(word)) { wordCounts[word]++; } }); }); // Find the most common significant words const commonWords = Object.entries(wordCounts) .filter(([_, count]) => count > typeNames.length / 2) .sort(([_, countA], [__, countB]) => countB - countA) .map(([word]) => word); if (commonWords.length > 0) { // Capitalize first letter of each word const nameParts = commonWords .slice(0, 2) // Take at most 2 words .map((word) => word.charAt(0).toUpperCase() + word.slice(1)); return nameParts.join("") + "Base"; } // If no common words, use the first name + Base return typeNames[0] + "Base"; } /** * Find the common directory ancestor for a list of file paths */ static findCommonDirectory(filePaths) { if (filePaths.length === 0) return ""; if (filePaths.length === 1) return path_browserify_1.default.dirname(filePaths[0]); const dirs = filePaths.map((filePath) => path_browserify_1.default.dirname(filePath).split(path_browserify_1.default.sep)); let commonDir = []; // Find common prefix of all directory paths const firstDir = dirs[0]; for (let i = 0; i < firstDir.length; i++) { const segment = firstDir[i]; if (dirs.every((dir) => dir[i] === segment)) { commonDir.push(segment); } else { break; } } return commonDir.join(path_browserify_1.default.sep); } /** * Suggest a better name for a type based on its content and usage */ static suggestBetterTypeName(typeDef, componentContext) { const currentName = typeDef.name; // Don't rename already good names if (currentName.length > 10 && !this.isGenericName(currentName)) { return currentName; } // If it's a props type for a component, suggest ComponentNameProps if (currentName.endsWith("Props") && componentContext) { const componentName = componentContext.replace(/\.(tsx|jsx|ts|js)$/, ""); return `${componentName}Props`; } // For interfaces, suggest ITypeName if not already using this convention if (!currentName.startsWith("I") && /^[A-Z]/.test(currentName) && typeDef.node.kind === 230 // InterfaceDeclaration kind ) { return `I${currentName}`; } // For type aliases with Props suffix but not tied to components if (currentName === "Props" || currentName === "Properties") { // Try to infer from file path const fileName = path_browserify_1.default.basename(typeDef.filePath, path_browserify_1.default.extname(typeDef.filePath)); if (fileName && fileName !== "index" && fileName !== "types") { const baseName = fileName.charAt(0).toUpperCase() + fileName.slice(1); return `${baseName}Props`; } } // Suggest more specific names for generic types if (this.isGenericName(currentName)) { const props = Array.from(typeDef.signature.properties.keys()); if (props.length > 0) { // Try to derive name from properties const significantProps = props.filter((p) => p.length > 3 && !["id", "name", "type", "value"].includes(p)); if (significantProps.length > 0) { const domain = this.inferDomainFromProps(significantProps); if (domain) { return `${domain}${this.getTypeSuffix(currentName)}`; } } } } return currentName; } /** * Check if a type name is too generic */ static isGenericName(name) { const genericNames = [ "Options", "Config", "Props", "State", "Data", "Type", "Request", "Response", "Result", "Item", "Info", "Details", ]; return (genericNames.includes(name) || (name.length < 10 && genericNames.some((g) => name.endsWith(g)))); } /** * Get type suffix (Props, Options, etc.) from type name */ static getTypeSuffix(name) { const suffixes = [ "Props", "Options", "Config", "State", "Data", "Type", "Result", ]; for (const suffix of suffixes) { if (name.endsWith(suffix)) { return suffix; } } return name; } /** * Infer domain name from properties */ static inferDomainFromProps(props) { // Try to guess domain from property names const domains = new Map(); // Look for domain-specific patterns in props const userProps = props.filter((p) => p.includes("user") || p.includes("name") || p.includes("email") || p.includes("password") || p.includes("avatar")); const productProps = props.filter((p) => p.includes("product") || p.includes("price") || p.includes("sku") || p.includes("inventory") || p.includes("stock")); const authProps = props.filter((p) => p.includes("token") || p.includes("auth") || p.includes("permission") || p.includes("role") || p.includes("access")); if (userProps.length >= 2) domains.set("User", userProps.length); if (productProps.length >= 2) domains.set("Product", productProps.length); if (authProps.length >= 2) domains.set("Auth", authProps.length); // Return the domain with most matches let bestDomain = null; let bestCount = 0; domains.forEach((count, domain) => { if (count > bestCount) { bestDomain = domain; bestCount = count; } }); return bestDomain; } /** * Determine if a name is better than another */ static isBetterName(name1, name2) { // Prefer longer, more specific names if (name1.length > name2.length && name1.length > 6) { return true; } // Prefer names with camelCase or PascalCase const hasCamelCase1 = /[a-z][A-Z]/.test(name1); const hasCamelCase2 = /[a-z][A-Z]/.test(name2); if (hasCamelCase1 && !hasCamelCase2) { return true; } // Prefer names without generic suffixes const genericSuffixes = ["Data", "Info", "Type", "Object"]; const hasGenericSuffix1 = genericSuffixes.some((s) => name1.endsWith(s)); const hasGenericSuffix2 = genericSuffixes.some((s) => name2.endsWith(s)); if (!hasGenericSuffix1 && hasGenericSuffix2) { return true; } return false; } } exports.NamingUtils = NamingUtils;