sicua
Version:
A tool for analyzing project structure and dependencies
210 lines (209 loc) • 8.98 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeSignatureUtils = void 0;
const typescript_1 = __importDefault(require("typescript"));
/**
* Utilities for generating and manipulating type signatures
*/
class TypeSignatureUtils {
/**
* Generate a structural signature for a type node
*/
static generateTypeSignature(node) {
const properties = new Map();
const methods = new Map();
const extendsTypes = [];
// Extract properties, methods, and extended types based on node kind
if (typescript_1.default.isInterfaceDeclaration(node)) {
// Handle extends clauses
if (node.heritageClauses) {
for (const clause of node.heritageClauses) {
if (clause.token === typescript_1.default.SyntaxKind.ExtendsKeyword) {
for (const type of clause.types) {
extendsTypes.push(type.getText());
}
}
}
}
// Handle properties and methods
for (const member of node.members) {
if (typescript_1.default.isPropertySignature(member) && member.name) {
const name = member.name.getText();
const type = member.type ? member.type.getText() : "any";
properties.set(name, type);
}
else if (typescript_1.default.isMethodSignature(member) && member.name) {
const name = member.name.getText();
const returnType = member.type ? member.type.getText() : "any";
const params = member.parameters.map((p) => p.getText()).join(", ");
methods.set(name, `(${params}) => ${returnType}`);
}
}
}
else if (typescript_1.default.isTypeAliasDeclaration(node)) {
if (typescript_1.default.isTypeLiteralNode(node.type)) {
// Handle object type aliases
for (const member of node.type.members) {
if (typescript_1.default.isPropertySignature(member) && member.name) {
const name = member.name.getText();
const type = member.type ? member.type.getText() : "any";
properties.set(name, type);
}
else if (typescript_1.default.isMethodSignature(member) && member.name) {
const name = member.name.getText();
const returnType = member.type ? member.type.getText() : "any";
const params = member.parameters.map((p) => p.getText()).join(", ");
methods.set(name, `(${params}) => ${returnType}`);
}
}
}
// For union/intersection types, we would need more complex logic
}
else if (typescript_1.default.isClassDeclaration(node)) {
// Handle extends
if (node.heritageClauses) {
for (const clause of node.heritageClauses) {
if (clause.token === typescript_1.default.SyntaxKind.ExtendsKeyword) {
for (const type of clause.types) {
extendsTypes.push(type.getText());
}
}
}
}
// Handle properties and methods
for (const member of node.members) {
if (typescript_1.default.isPropertyDeclaration(member) && member.name) {
const name = member.name.getText();
const type = member.type ? member.type.getText() : "any";
properties.set(name, type);
}
else if (typescript_1.default.isMethodDeclaration(member) && member.name) {
const name = member.name.getText();
const returnType = member.type ? member.type.getText() : "any";
const params = member.parameters.map((p) => p.getText()).join(", ");
methods.set(name, `(${params}) => ${returnType}`);
}
}
}
// Generate a unique signature hash from the structure
const propsArray = Array.from(properties.entries()).sort(([a], [b]) => a.localeCompare(b));
const methodsArray = Array.from(methods.entries()).sort(([a], [b]) => a.localeCompare(b));
const signatureString = JSON.stringify({
props: propsArray,
methods: methodsArray,
extends: extendsTypes.sort(),
});
const signatureHash = this.hashString(signatureString);
return {
kind: node.kind,
properties,
methods,
extends: extendsTypes,
signature: signatureHash,
};
}
/**
* Simple hash function for strings
*/
static hashString(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash |= 0; // Convert to 32bit integer
}
return hash.toString(16);
}
/**
* Compare two type signatures for structural equality
*/
static areSignaturesEqual(a, b) {
return a.signature === b.signature;
}
/**
* Check if a type is a subtype of another by signature
*/
static isSubtypeOf(subType, superType) {
// A type is a subtype if it has all properties of the supertype
// and potentially more
for (const [propName, propType] of superType.properties.entries()) {
if (!subType.properties.has(propName)) {
return false;
}
// For strict subtyping, would need to check type compatibility too
// For now, we just check if the property exists
}
// Check methods too
for (const [methodName, methodSig] of superType.methods.entries()) {
if (!subType.methods.has(methodName)) {
return false;
}
}
return true;
}
/**
* Get property overlap between two type signatures
*/
static getPropertyOverlap(a, b) {
const propsA = Array.from(a.properties.keys());
const propsB = Array.from(b.properties.keys());
const common = propsA.filter((prop) => propsB.includes(prop));
const onlyInA = propsA.filter((prop) => !propsB.includes(prop));
const onlyInB = propsB.filter((prop) => !propsA.includes(prop));
return { common, onlyInA, onlyInB };
}
/**
* Create a merged signature from multiple type signatures
*/
static mergeTypeSignatures(signatures) {
if (signatures.length === 0) {
throw new Error("Cannot merge empty array of signatures");
}
const merged = {
kind: signatures[0].kind,
properties: new Map(),
methods: new Map(),
extends: [],
signature: "",
};
// Merge all properties, methods, and extends
for (const sig of signatures) {
// Add properties
for (const [propName, propType] of sig.properties.entries()) {
if (!merged.properties.has(propName)) {
merged.properties.set(propName, propType);
}
else if (merged.properties.get(propName) !== propType) {
// If property exists with different type, use union type
merged.properties.set(propName, `${merged.properties.get(propName)} | ${propType}`);
}
}
// Add methods
for (const [methodName, methodSig] of sig.methods.entries()) {
if (!merged.methods.has(methodName)) {
merged.methods.set(methodName, methodSig);
}
}
// Add extends
for (const ext of sig.extends) {
if (!merged.extends.includes(ext)) {
merged.extends.push(ext);
}
}
}
// Regenerate signature hash
const propsArray = Array.from(merged.properties.entries()).sort(([a], [b]) => a.localeCompare(b));
const methodsArray = Array.from(merged.methods.entries()).sort(([a], [b]) => a.localeCompare(b));
const signatureString = JSON.stringify({
props: propsArray,
methods: methodsArray,
extends: merged.extends.sort(),
});
merged.signature = this.hashString(signatureString);
return merged;
}
}
exports.TypeSignatureUtils = TypeSignatureUtils;