UNPKG

apisurf

Version:

Analyze API surface changes between npm package versions to catch breaking changes

146 lines (145 loc) 4.62 kB
/** * Parse function parameters from a parameter string, handling nested types correctly */ export function parseParameters(paramString) { if (!paramString || paramString.trim() === '') { return []; } const parameters = []; let current = ''; let parenDepth = 0; let bracketDepth = 0; let braceDepth = 0; let angleDepth = 0; let inString = false; let stringChar = ''; for (let i = 0; i < paramString.length; i++) { const char = paramString[i]; const prevChar = i > 0 ? paramString[i - 1] : ''; // Handle string literals if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') { if (!inString) { inString = true; stringChar = char; } else if (char === stringChar) { inString = false; } } // Skip string content for bracket counting if (!inString) { // Track depth for different bracket types separately switch (char) { case '(': parenDepth++; break; case ')': parenDepth--; break; case '[': bracketDepth++; break; case ']': bracketDepth--; break; case '{': braceDepth++; break; case '}': braceDepth--; break; case '<': angleDepth++; break; case '>': // Only decrease if not part of => and angle depth is positive if (angleDepth > 0 && (i === paramString.length - 1 || paramString[i - 1] !== '=')) { angleDepth--; } break; } // Split on comma only when all depths are 0 if (char === ',' && parenDepth === 0 && bracketDepth === 0 && braceDepth === 0 && angleDepth === 0) { parameters.push(current.trim()); current = ''; continue; } } current += char; } // Don't forget the last parameter if (current.trim()) { parameters.push(current.trim()); } return parameters; } /** * Check if a parameter is optional (has ? or default value) */ export function isOptionalParameter(param) { // Check for optional marker (?) if (param.includes('?:')) return true; // Check for default value (=) // But make sure it's not part of an arrow function (=>) const equalIndex = param.indexOf('='); if (equalIndex > 0) { const afterEqual = param.substring(equalIndex + 1); // If the next character is >, it's an arrow function, not a default value if (!afterEqual.trimStart().startsWith('>')) { return true; } } return false; } /** * Extract parameter name from a parameter string */ export function getParameterName(param) { // Handle destructured parameters if (param.startsWith('{') || param.startsWith('[')) { return param; // Return the whole destructured pattern } // Regular parameters: name: type const colonIndex = param.indexOf(':'); if (colonIndex > 0) { return param.substring(0, colonIndex).replace('?', '').trim(); } // Parameters without type annotation return param.trim(); } /** * Extract parameter type from a parameter string */ export function getParameterType(param) { const colonIndex = param.indexOf(':'); if (colonIndex === -1) return 'any'; let type = param.substring(colonIndex + 1).trim(); // Remove default value if present, but not arrow functions let i = 0; let parenDepth = 0; let defaultStart = -1; while (i < type.length) { if (type[i] === '(') parenDepth++; else if (type[i] === ')') parenDepth--; else if (type[i] === '=' && parenDepth === 0) { // Check if it's an arrow function if (i + 1 < type.length && type[i + 1] === '>') { i++; // Skip the arrow } else { // It's a default value defaultStart = i; break; } } i++; } if (defaultStart > 0) { type = type.substring(0, defaultStart).trim(); } return type || 'any'; }