UNPKG

apisurf

Version:

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

110 lines (109 loc) 4.04 kB
/** * Parse type alias definition from source lines */ export function parseTypeDefinition(lines, startIndex, name) { const properties = []; let braceCount = 0; let inTypeDef = false; let isObjectType = false; let signature = ''; // Get the full line with the type definition const fullLine = lines[startIndex]; // Check if this is a function type alias const functionTypeMatch = fullLine.match(/export\s+type\s+\w+.*?=\s*(?:<[^>]+>\s*)?\(/); if (functionTypeMatch) { // Extract the full function signature let fullSignature = fullLine.trim(); // Handle multi-line function types if (!fullSignature.includes(';')) { for (let i = startIndex + 1; i < lines.length; i++) { const nextLine = lines[i].trim(); fullSignature += ' ' + nextLine; if (nextLine.includes(';')) { break; } } } // Keep the signature as-is, including semicolon fullSignature = fullSignature.trim(); return { name, kind: 'type', signature: fullSignature }; } // Check for mapped types or other non-object types first if (fullLine.includes('[') && fullLine.includes(' in ')) { // This is a mapped type let fullSig = fullLine.trim(); if (!fullSig.includes(';')) { for (let j = startIndex + 1; j < lines.length; j++) { fullSig += ' ' + lines[j].trim(); if (lines[j].includes(';')) { break; } } } return { name, kind: 'type', extendedProperties: undefined, signature: fullSig }; } for (let i = startIndex; i < lines.length; i++) { const line = lines[i].trim(); // Check if this is an object type definition if (i === startIndex && line.includes('= {') && !line.includes('[')) { isObjectType = true; } // Count braces const openBraces = (line.match(/{/g) || []).length; const closeBraces = (line.match(/}/g) || []).length; if (openBraces > 0 && isObjectType) { braceCount += openBraces; inTypeDef = true; } // Only parse properties at the top level (braceCount === 1) if (inTypeDef && braceCount === 1 && !line.includes('{') && !line.includes('}')) { // Match properties like: propertyName: type; or propertyName?: type; // This includes arrow function properties like: status?: () => string; const propMatch = line.match(/^\s*(\w+)(\?)?:\s*/); if (propMatch && !line.includes('[')) { properties.push({ name: propMatch[1], required: !propMatch[2] }); } // Match method signatures like: methodName(): void; else if (line.match(/^\s*(\w+)(\?)?\s*\(/)) { const methodMatch = line.match(/^\s*(\w+)(\?)?\s*\(/); if (methodMatch) { properties.push({ name: methodMatch[1], required: !methodMatch[2] }); } } } if (closeBraces > 0 && isObjectType) { braceCount -= closeBraces; if (braceCount === 0) { break; } } // For non-object types, capture the full type definition if (!isObjectType) { signature = fullLine.trim(); break; } } return { name, kind: 'type', extendedProperties: isObjectType ? properties : undefined, signature: isObjectType ? `export type ${name} = { ${properties.map(p => `${p.name}${p.required ? '' : '?'}: any`).join('; ')} }` : signature || `export type ${name} = any` }; }