UNPKG

whistle.capimock

Version:

连接capi的whistle插件

218 lines (185 loc) 7.48 kB
var ts = require('typescript'); var fs = require('fs'); const path = require('path'); const { Project, SyntaxKind } = require('ts-morph'); function generateExampleValue(type) { switch (type) { case 'string': return 'sample_string'; case 'number': return 123; case 'boolean': return true; case 'array': return [generateExampleValue('string')]; // 假设数组元素为字符串类型 default: return 'unknown_type_example'; } } function parseType(type, schema) { const properties = {}; if (type.isClass()) { type.getProperties().forEach(prop => { const propType = type.getPropertyType(prop); properties[prop.getName()] = parseType(propType, schema); }); } else if (type.isArray()) { const elementType = type.getArrayElementType(); properties['items'] = parseType(elementType, schema); } else if (type.isString()) { properties['type'] = 'string'; properties['example'] = generateExampleValue('string'); } else if (type.isNumber()) { properties['type'] = 'number'; properties['example'] = generateExampleValue('number'); } else if (type.isBoolean()) { properties['type'] = 'boolean'; properties['example'] = generateExampleValue('boolean'); } else if (type.isEnum()) { properties['type'] = 'string'; properties['enum'] = type.getEnumMembers().map(member => member.getText()); properties['example'] = properties['enum'][0]; } else { const typeName = type.getText(); if (typeName.startsWith('import("')) { const endIndex = typeName.indexOf('")'); const importPath = typeName.slice(8, endIndex); const importResult = /^import\(([^)]+)\)$/.exec(typeName.split('.')[0]); if (importResult) { const importFile = importResult[1].replace(/['"]/g, ''); const typeNameWithoutImport = typeName.slice(endIndex + 3); const fullPath = path.resolve(__dirname, importFile); if (fs.existsSync(fullPath)) { const importedSchema = parseTsFile(fullPath); if (importedSchema['components'] && importedSchema['components']['schemas']) { Object.assign(schema['components']['schemas'], importedSchema['components']['schemas']); } if (schema['components'] && schema['components']['schemas'][typeNameWithoutImport]) { properties['$ref'] = `#/components/schemas/${typeNameWithoutImport}`; } else { properties['type'] = typeNameWithoutImport; } } else { properties['type'] = typeName; } } else { console.log('No match found for importPath:', importPath); properties['type'] = typeName; } } else { if (schema['components'] && schema['components']['schemas'][typeName]) { properties['$ref'] = `#/components/schemas/${typeName}`; } else { properties['type'] = typeName; } } } return properties; } function parseInterface(node, schema) { const properties = {}; node.getMembers().forEach(member => { const memberType = member.getType(); const memberProperties = parseType(memberType, schema); memberProperties['example'] = generateExampleValue(memberProperties['type']); // 添加示例值 properties[member.getName()] = memberProperties; }); if (!schema['components']) { schema['components'] = {}; } schema['components']['schemas'][node.getName()] = { properties }; } function generateExampleForSchema(properties) { const example = {}; for (const key in properties) { const property = properties[key]; if (property.type && property.type !== 'object') { example[key] = generateExampleValue(property.type); } else if (property.properties) { example[key] = generateExampleForSchema(property.properties); } } return example; } function parseFunction(node, schema) { const returnType = node.getReturnType(); const functionName = node.getName(); if (returnType && functionName) { let schemaRef = returnType.getText(); if (schemaRef.startsWith('Promise<')) { schemaRef = schemaRef.slice(8, -1); } const interfaceSchema = schema['components']['schemas'][schemaRef.split('.')[1]]; if (interfaceSchema && interfaceSchema.properties) { schema['paths'][`/${functionName}`] = { 'get': { 'responses': { '200': { 'description': 'Success', 'content': { 'application/json': { 'schema': { type: 'object', properties: { ...interfaceSchema.properties }, example: generateExampleForSchema(interfaceSchema.properties) // 添加示例值 } } } } } } }; } else { schema['paths'][`/${functionName}`] = { 'get': { 'responses': { '200': { 'description': 'Success', 'content': { 'application/json': { 'schema': { $ref: `#/components/schemas/${schemaRef}` } } } } } } }; } } } function parseTsFile(file) { const project = new Project(); const sourceFile = project.addSourceFileAtPath(file); const schema = { paths: {}, components: { schemas: {} }, // path: '' }; sourceFile.getDescendantsOfKind(SyntaxKind.InterfaceDeclaration).forEach(node => { parseInterface(node, schema); }); sourceFile.getDescendantsOfKind(SyntaxKind.FunctionDeclaration).forEach(node => { parseFunction(node, schema); }); // 自动识别所有的 type 类型并处理它们 const typeDeclarations = sourceFile.getDescendantsOfKind(SyntaxKind.TypeAliasDeclaration); typeDeclarations.forEach(typeDeclaration => { const typeName = typeDeclaration.getName(); const typeSchema = parseType(typeDeclaration.getType(), schema); // 如果类型为空,则不添加到模式中 if (!isEmpty(typeSchema)) { schema['components']['schemas'][typeName] = typeSchema; } }); return schema; } function isEmpty(schema) { return schema.type === 'object' && !schema.properties; } module.exports = parseTsFile