mirror of
https://github.com/hpd840321/starRiverProperty.git
synced 2026-06-09 08:20:31 +08:00
feat: add API call extractor script
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Former-commit-id: 32eb1a04435e3e1adf760c8a9bd3f499f23e4c3a
This commit is contained in:
@@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**
|
||||||
|
* API 调用提取器
|
||||||
|
* 从格式化后的 JS 中提取 HTTP 请求模式,输出 API 清单 JSON
|
||||||
|
*
|
||||||
|
* 用法: node extract-api-calls.js <formatted-dir> <output.json>
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const parser = require('@babel/parser');
|
||||||
|
const traverse = require('@babel/traverse').default;
|
||||||
|
|
||||||
|
function extractApiCalls(inputDir, outputFile) {
|
||||||
|
const apiCalls = [];
|
||||||
|
const files = fs.readdirSync(inputDir).filter(f => f.endsWith('.formatted.js'));
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const code = fs.readFileSync(path.join(inputDir, file), 'utf-8');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const ast = parser.parse(code, {
|
||||||
|
sourceType: 'script',
|
||||||
|
plugins: ['jsx', 'typescript', 'classProperties', 'dynamicImport'],
|
||||||
|
errorRecovery: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
traverse(ast, {
|
||||||
|
// Match: axios.get('/api/xxx'), axios.post('/api/xxx', data)
|
||||||
|
CallExpression(nodePath) {
|
||||||
|
const node = nodePath.node;
|
||||||
|
const callee = node.callee;
|
||||||
|
|
||||||
|
if (
|
||||||
|
callee.type === 'MemberExpression' &&
|
||||||
|
callee.object.type === 'Identifier' &&
|
||||||
|
(callee.object.name === 'axios' || callee.object.name === 'http')
|
||||||
|
) {
|
||||||
|
const method = callee.property.name;
|
||||||
|
if (['get', 'post', 'put', 'delete', 'patch'].includes(method)) {
|
||||||
|
const url = node.arguments[0];
|
||||||
|
if (url && url.type === 'StringLiteral') {
|
||||||
|
apiCalls.push({
|
||||||
|
file: file,
|
||||||
|
method: method.toUpperCase(),
|
||||||
|
url: url.value,
|
||||||
|
hasBody: method !== 'get' && node.arguments.length > 1,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.$http.get|post
|
||||||
|
if (
|
||||||
|
callee.type === 'MemberExpression' &&
|
||||||
|
callee.object.type === 'MemberExpression' &&
|
||||||
|
callee.object.property.name === '$http'
|
||||||
|
) {
|
||||||
|
const method = callee.property.name;
|
||||||
|
const url = node.arguments[0];
|
||||||
|
if (url && url.type === 'StringLiteral') {
|
||||||
|
apiCalls.push({
|
||||||
|
file: file,
|
||||||
|
method: method.toUpperCase(),
|
||||||
|
url: url.value,
|
||||||
|
via: '$http',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Match string constants containing API paths
|
||||||
|
StringLiteral(nodePath) {
|
||||||
|
const value = nodePath.node.value;
|
||||||
|
if (typeof value === 'string' && value.startsWith('/api/') && value.length > 5) {
|
||||||
|
const parent = nodePath.parent;
|
||||||
|
if (parent.type === 'CallExpression') return;
|
||||||
|
|
||||||
|
apiCalls.push({
|
||||||
|
file: file,
|
||||||
|
method: 'REFERENCE',
|
||||||
|
url: value,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`解析失败: ${file} — ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deduplicate + sort
|
||||||
|
const unique = {};
|
||||||
|
apiCalls.forEach(call => {
|
||||||
|
const key = `${call.method} ${call.url}`;
|
||||||
|
if (!unique[key]) {
|
||||||
|
unique[key] = call;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = Object.values(unique).sort((a, b) => a.url.localeCompare(b.url));
|
||||||
|
|
||||||
|
fs.writeFileSync(outputFile, JSON.stringify(result, null, 2));
|
||||||
|
console.log(`提取完成: ${result.length} 个 API 端点 → ${outputFile}`);
|
||||||
|
|
||||||
|
// Also generate Markdown report
|
||||||
|
const mdPath = outputFile.replace('.json', '.md');
|
||||||
|
let md = '# API 端点清单\n\n';
|
||||||
|
md += `| Method | URL | 来源文件 |\n`;
|
||||||
|
md += `|--------|-----|----------|\n`;
|
||||||
|
result.forEach(call => {
|
||||||
|
md += '| ' + call.method + ' | `' + call.url + '` | ' + call.file + ' |\n';
|
||||||
|
});
|
||||||
|
fs.writeFileSync(mdPath, md);
|
||||||
|
console.log(`Markdown 清单: ${mdPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const args = process.argv.slice(2);
|
||||||
|
if (args.length < 2) {
|
||||||
|
console.log('用法: node extract-api-calls.js <formatted-dir> <output.json>');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
extractApiCalls(args[0], args[1]);
|
||||||
Reference in New Issue
Block a user