node-postman-generator
Version:
📦 CLI tool that automatically converts Express.js routes to Postman collections - handles path/query/body params, and generates ready-to-import JSON with route config.
235 lines (204 loc) • 7.14 kB
JavaScript
function parseComment(comment) {
const meta = {
group: 'Default',
name: '',
description: '',
params: [],
headers: [],
body: [],
method: '',
skip: false
};
let collectingGroup = {
active: false,
type: '',
content: []
};
const GROUP_CONFIGS = {
'@apiQueryGroup': {
type: 'query',
target: 'params'
},
'@apiParamGroup': {
type: 'param',
target: 'params'
},
'@apiBody': {
type: 'body',
target: 'body'
}
};
// 处理单行group内容的通用函数
// Single-line writing
/**
* @apiParamGroup [{"type": "String", "name": "username", "description": "Username"}]
*/
// multi-line writing
/**
* @apiParamGroup [
* {"type": "String", "name": "username", "description": "Username"},
* {"type": "Number", "name": "age", "description": "User age", "optional": true}
* ]
*/
const processGroupContent = (content, groupConfig) => {
// Check if using old format
if (content.startsWith('[[') && content.endsWith(']]')) {
console.warn(`[Comment Parser] Deprecated: The [[]] format will be removed in version 2.8.0.
Please use the new JSON array format instead:
Example:
@apiParamGroup [
{"type": "String", "name": "username", "description": "Username"},
{"type": "Number", "name": "age", "description": "User age", "optional": true, "defaultValue": 18}
]
Note: If you need to use the old [[]] format, please use version < 2.8.0
CHANGELOG guide: https://github.com/KelvinTee58/node-postman-generator/blob/main/doc/CHANGELOG.md#280`);
return;
}
try {
// Try to parse JSON array
let params;
try {
params = JSON.parse(content);
if (!Array.isArray(params)) {
throw new Error('Parameter format must be an array');
}
} catch (e) {
console.warn(`[Comment Parser] Invalid JSON format in ${groupConfig.type}: "${content}"`);
return;
}
params.forEach(param => {
if (!param.type || !param.name) {
console.warn('[Comment Parser] Missing required fields: type or name');
return;
}
// console.log('param.defaultValue :>> ', param.defaultValue);
const processedParam = {
// 根据类型设置 in
in: groupConfig.type === 'body' ? 'body' :
groupConfig.type === 'query' ? 'query' : 'path',
name: param.name,
type: param.type,
description: param.description || '',
defaultValue: param.defaultValue,
optional: param.optional || false
};
// 根据配置决定添加到哪个目标数组
meta[groupConfig.target].push(processedParam);
});
} catch (error) {
console.warn(`[Comment Parser] Error processing ${groupConfig.type}: ${error.message}`);
}
};
(comment || '').split('\n').forEach(line => {
line = line.replace(/^\s*\*\s?/, '').trim();
// 修改多行内容收集的判断条件
if (collectingGroup.active) {
if (line.includes(']')) { // 改为检测 ] 而不是 ]]
collectingGroup.content.push(line);
const fullContent = collectingGroup.content.join('\n') // 改用 \n 连接
.replace(/\/\*\*|\*\//g, '') // 移除注释标记
.replace(/^\s*\*\s?/gm, '') // 移除每行开头的星号
.trim();
processGroupContent(fullContent, GROUP_CONFIGS[collectingGroup.type]); // 使用 存储多行结束调用processGroupContent
collectingGroup = { active: false, type: '', content: [] };
} else {
collectingGroup.content.push(line);
}
return;
}
// 检查是否是group开始
const groupType = Object.keys(GROUP_CONFIGS).find(prefix => line.startsWith(prefix));
if (groupType) {
const content = line.replace(new RegExp(`${groupType}\\s+`), '').trim();
const groupConfig = GROUP_CONFIGS[groupType];
// 如果是完整的单行 JSON
if (content.startsWith('[') && content.endsWith(']')) {
processGroupContent(content, groupConfig);
} else if (content.startsWith('[')) {
// 开始多行收集
collectingGroup = {
active: true,
type: groupType,
content: [content]
};
}
return;
}
// 跳过标记检测
if (line === '@postman-skip') {
meta.skip = true;
}
// @api {get} /path Description
if (line.startsWith('@api')) {
const match = line.match(/@api\s+{(\w+)}\s+(\S+)\s*(.*)/);
if (match) {
meta.method = match[1];
meta.path = match[2];
meta.description = match[3] || '';
}
}
// @apiParam {Number} name Description (必选参数,没有方括号)
// @apiParam {Number} [name=defaultValue] Description (有方括号和默认值)
// @apiParam {String} [name] Description (可选参数,只有方括号,没有默认值)
if (line.startsWith('@apiParam ')) {
const match = line.match(/@apiParam\s+{(\w+)}\s+(?:\[(\w+)(?:=(\S+))?\]|(\S+))\s+(.*)/);
if (match) {
let paramType = match[1];
let paramName = match[2] || match[4];
let paramDefaultValue = match[3];
let paramDescription = match[5];
let isOptional = !!match[2];
meta.params.push({
in: 'path',
name: paramName,
type: paramType,
description: paramDescription,
defaultValue: paramDefaultValue,
optional: isOptional
});
}
}
// @apiQuery {Number} name Description
// @apiQuery {Number} [name=defaultValue] Description
// @apiQuery {String} [name] Description
if (line.startsWith('@apiQuery ')) {
const match = line.match(/@apiQuery\s+{(\w+)}\s+(?:\[(\w+)(?:=(\S+))?\]|(\S+))\s+(.*)/);
if (match) {
let paramType = match[1];
let paramName = match[2] || match[4]; // [name=defaultValue] or name
let paramDefaultValue = match[3]; // defaultValue
let paramDescription = match[5] || ''; // Handle empty description
let isOptional = !!match[2]; // Check for brackets []
meta.params.push({
in: 'query',
name: paramName,
type: paramType,
description: paramDescription,
defaultValue: paramDefaultValue,
optional: isOptional
});
}
}
// @apiHeader {String} Authorization Token description
if (line.startsWith('@apiHeader')) {
const match = line.match(/@apiHeader\s+{(\w+)}\s+(\S+)\s+(.*)/);
if (match) {
meta.headers.push({
name: match[2],
type: match[1],
description: match[3]
});
}
}
// @apiGroup {Object} Group name
if (line.startsWith('@apiGroup')) {
meta.group = line.replace('@apiGroup', '').trim();
}
// @apiName {Object} api name
if (line.startsWith('@apiName')) {
meta.name = line.replace('@apiName', '').trim();
}
});
return meta;
}
module.exports = parseComment;