whistle.capimock
Version:
连接capi的whistle插件
218 lines (185 loc) • 7.48 kB
JavaScript
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