swagger-typescript-api命名规则修改

109 阅读2分钟

通过swagger-typescript-api获取的接口方法,默认的命名方式是后端的方法名,后端经常会在不同的controller下写很多相同的方法名(如list这种命名) 通过自定义调用generateApi,可以将命名规则改为 请求方法+路径

/* eslint-disable */
import path from 'node:path'
import { generateApi } from 'swagger-typescript-api'

const args = process.argv.slice(2)
const target = args[0]

function capitalize(str) {
	return str.charAt(0).toUpperCase() + str.slice(1)
}

function toPascalCase(str) {
	const words = String(str)
		.split(/[^a-zA-Z0-9]+/)
		.filter(Boolean)
	return words.map((w) => capitalize(w)).join('')
}

function toCamelCase(str) {
	const pascal = toPascalCase(str)
	if (!pascal) return ''
	return pascal.charAt(0).toLowerCase() + pascal.slice(1)
}

function toSafeRouteName(name) {
	const safe = toCamelCase(String(name).replace(/[^a-zA-Z0-9]+/g, ' '))
	return safe || 'request'
}

function singular(word) {
	// 简易处理,可换成更完善的库(如 pluralize)
	if (word.endsWith('s')) return word.slice(0, -1)
	return word
}

function joinSegments(segments) {
	return segments.join('')
}

function main() {
	generateApi({
		name: 'ApiMailbox.ts',
		fileName: 'ApiMailbox.ts',
		// set to `false` to prevent the tool from writing to disk
		url: 'http://127.0.0.1:3000/swagger-doc-json',
		output: path.resolve(process.cwd(), './src/apiMailbox'),
		httpClientType: 'axios',
		generateRouteTypes: false,
		hooks: {
			onFormatRouteName: (routeInfo, templateRouteName) => {
				/**
				 * routeInfo 示例:
				 * {
				 *   method: 'get',
				 *   route: '/users/{id}/roles',
				 *   originalPath: '/users/{id}/roles',
				 *   ... 其他字段
				 * }
				 */
				const httpMethod = (routeInfo.method || '').toLowerCase()
				const rawPath = routeInfo.route || ''

				const segmentMeta = rawPath
					.split('/')
					.filter(Boolean)
					.map((segment) => {
						const isParam = /^\{.+\}$/.test(segment)
						const raw = segment.replace(/^\{|\}$/g, '')
						return {
							isParam,
							raw,
							name: toPascalCase(raw)
						}
					})

				const resourceSegments = segmentMeta.filter((s) => !s.isParam).map((s) => s.name)

				// 简单判断动作(可按需扩展更复杂逻辑)
				const verbMap = {
					get: 'get',
					post: 'create',
					put: 'update',
					patch: 'patch',
					delete: 'delete'
				}

				// 特殊:嵌套 + POST 往往表示“添加子资源”
				// 例如 /users/{id}/roles POST -> addUserRole
				if (httpMethod === 'post' && segmentMeta.length > 2 && resourceSegments.length >= 2) {
					// 最后一个非参数段作为子资源
					const last = resourceSegments[resourceSegments.length - 1]
					return toSafeRouteName('add' + joinSegments(resourceSegments.slice(0, 1)) + singular(last))
				}

				// 构造核心资源名(主资源+可能的子资源)
				let core = resourceSegments.map(capitalize).join('')
				if (!core) {
					core = 'Request'
				}

				// 拼接参数修饰(ByXxx...)
				let byPart = ''
				const paramNames = segmentMeta.filter((s) => s.isParam).map((s) => s.name)
				if (paramNames.length) {
					byPart = 'By' + paramNames.map(capitalize).join('And')
				}

				// 选择动词
				let verb = verbMap[httpMethod] || httpMethod

				// 如果是 GET 并且无参数且资源名复数 -> getUsers 这种就保留
				// 如果是 GET + 有参数 -> getUserById
				// 如果是 DELETE + 有子资源参数 -> deleteUserRoleByRoleId
				// 已在通用逻辑覆盖

				const customName = verb + core + byPart
				const finalName = customName || templateRouteName || `${httpMethod}Request`
				return toSafeRouteName(finalName)
			}
		},
		prettier: {
			// By default prettier config is load from your project
			printWidth: 120,
			tabWidth: 2,
			trailingComma: 'all',
			parser: 'typescript'
		}
	})
}
main()