mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-22 20:04:01 +00:00
chore: openapi doc generator (#2644)
* chore: extract the type and comment from apis * chore: template code * feat: openapi * pref: openapi generator. send into public/openapi folder
This commit is contained in:
@@ -10,8 +10,7 @@
|
|||||||
"postinstall": "sh ./scripts/postinstall.sh",
|
"postinstall": "sh ./scripts/postinstall.sh",
|
||||||
"initIcon": "node ./scripts/icon/init.js",
|
"initIcon": "node ./scripts/icon/init.js",
|
||||||
"previewIcon": "node ./scripts/icon/index.js",
|
"previewIcon": "node ./scripts/icon/index.js",
|
||||||
"i18n:delete-unused-keys": "node ./scripts/i18n/delete-unused-keys.js",
|
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html"
|
||||||
"i18n:query": "node ./scripts/i18n/query.js"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@chakra-ui/cli": "^2.4.1",
|
"@chakra-ui/cli": "^2.4.1",
|
||||||
|
1229
pnpm-lock.yaml
generated
1229
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
443
projects/app/public/openapi/index.html
Normal file
443
projects/app/public/openapi/index.html
Normal file
File diff suppressed because one or more lines are too long
@@ -4,13 +4,25 @@ import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant
|
|||||||
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||||
export type OutLinkListQuery = {
|
|
||||||
appId: string;
|
export const ApiMetadata = {
|
||||||
type: string;
|
name: '获取应用内所有 Outlink',
|
||||||
|
author: 'Finley',
|
||||||
|
version: '0.1.0'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Outlink
|
||||||
|
export type OutLinkListQuery = {
|
||||||
|
appId: string; // 应用 ID
|
||||||
|
type: string; // 类型
|
||||||
|
};
|
||||||
|
|
||||||
export type OutLinkListBody = {};
|
export type OutLinkListBody = {};
|
||||||
|
|
||||||
|
// 响应: 应用内全部 Outlink
|
||||||
export type OutLinkListResponse = OutLinkSchema[];
|
export type OutLinkListResponse = OutLinkSchema[];
|
||||||
|
|
||||||
|
// 查询应用内全部 Outlink
|
||||||
async function handler(
|
async function handler(
|
||||||
req: ApiRequestProps<OutLinkListBody, OutLinkListQuery>
|
req: ApiRequestProps<OutLinkListBody, OutLinkListQuery>
|
||||||
): Promise<OutLinkListResponse> {
|
): Promise<OutLinkListResponse> {
|
||||||
|
1
scripts/openapi/.env
Normal file
1
scripts/openapi/.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SEARCH_PATH=support
|
3
scripts/openapi/.gitignore
vendored
Normal file
3
scripts/openapi/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
*.js
|
||||||
|
openapi.json
|
||||||
|
openapi.out
|
54
scripts/openapi/index.ts
Normal file
54
scripts/openapi/index.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { parseAPI } from './utils';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { convertOpenApi } from './openapi';
|
||||||
|
|
||||||
|
const rootPath = 'projects/app/src/pages/api';
|
||||||
|
const exclude = ['/admin', '/proApi'];
|
||||||
|
|
||||||
|
function getAllFiles(dir: string) {
|
||||||
|
let files: string[] = [];
|
||||||
|
const stat = fs.statSync(dir);
|
||||||
|
if (stat.isDirectory()) {
|
||||||
|
const list = fs.readdirSync(dir);
|
||||||
|
list.forEach((item) => {
|
||||||
|
const fullPath = path.join(dir, item);
|
||||||
|
if (!exclude.some((excluded) => fullPath.includes(excluded))) {
|
||||||
|
files = files.concat(getAllFiles(fullPath));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
files.push(dir);
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchPath = process.env.SEARCH_PATH || '';
|
||||||
|
|
||||||
|
const files = getAllFiles(path.join(rootPath, searchPath));
|
||||||
|
// console.log(files)
|
||||||
|
const apis = files.map((file) => {
|
||||||
|
return parseAPI({ path: file, rootPath });
|
||||||
|
});
|
||||||
|
|
||||||
|
const openapi = convertOpenApi({
|
||||||
|
apis,
|
||||||
|
openapi: '3.0.0',
|
||||||
|
info: {
|
||||||
|
title: 'FastGPT OpenAPI',
|
||||||
|
version: '1.0.0',
|
||||||
|
author: 'FastGPT'
|
||||||
|
},
|
||||||
|
servers: [
|
||||||
|
{
|
||||||
|
url: 'http://localhost:4000'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
const json = JSON.stringify(openapi, null, 2);
|
||||||
|
|
||||||
|
fs.writeFileSync('./scripts/openapi/openapi.json', json);
|
||||||
|
fs.writeFileSync('./scripts/openapi/openapi.out', JSON.stringify(apis, null, 2));
|
||||||
|
|
||||||
|
console.log('Total APIs:', files.length);
|
181
scripts/openapi/openapi.ts
Normal file
181
scripts/openapi/openapi.ts
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import { ApiType } from './type';
|
||||||
|
|
||||||
|
type OpenAPIParameter = {
|
||||||
|
name: string;
|
||||||
|
in: string;
|
||||||
|
description: string;
|
||||||
|
required: boolean;
|
||||||
|
schema: {
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type OpenAPIResponse = {
|
||||||
|
[code: string]: {
|
||||||
|
description?: string;
|
||||||
|
content: {
|
||||||
|
[mediaType: string]: {
|
||||||
|
schema: {
|
||||||
|
type: string;
|
||||||
|
properties?: {
|
||||||
|
[key: string]: {
|
||||||
|
type: string;
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type PathType = {
|
||||||
|
[method: string]: {
|
||||||
|
description: string;
|
||||||
|
parameters: OpenAPIParameter[];
|
||||||
|
responses: OpenAPIResponse;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
type PathsType = {
|
||||||
|
[url: string]: PathType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OpenApiType = {
|
||||||
|
openapi: string;
|
||||||
|
info: {
|
||||||
|
title: string;
|
||||||
|
version: string;
|
||||||
|
author: string;
|
||||||
|
};
|
||||||
|
paths: PathsType;
|
||||||
|
servers?: {
|
||||||
|
url: string;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function convertPath(api: ApiType): PathType {
|
||||||
|
const method = api.method.toLowerCase();
|
||||||
|
const parameters: any[] = [];
|
||||||
|
if (api.query) {
|
||||||
|
if (Array.isArray(api.query)) {
|
||||||
|
api.query.forEach((item) => {
|
||||||
|
parameters.push({
|
||||||
|
name: item.key,
|
||||||
|
description: item.comment,
|
||||||
|
in: 'query',
|
||||||
|
required: item.required,
|
||||||
|
schema: {
|
||||||
|
type: item.type
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
parameters.push({
|
||||||
|
description: api.query.comment,
|
||||||
|
name: api.query.key,
|
||||||
|
in: 'query',
|
||||||
|
required: api.query.required,
|
||||||
|
schema: {
|
||||||
|
type: api.query.type
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (api.body) {
|
||||||
|
if (Array.isArray(api.body)) {
|
||||||
|
api.body.forEach((item) => {
|
||||||
|
parameters.push({
|
||||||
|
description: item.comment,
|
||||||
|
name: item.key,
|
||||||
|
in: 'body',
|
||||||
|
required: item.required,
|
||||||
|
schema: {
|
||||||
|
type: item.type
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const responses: OpenAPIResponse = (() => {
|
||||||
|
if (api.response) {
|
||||||
|
if (Array.isArray(api.response)) {
|
||||||
|
const properties: {
|
||||||
|
[key: string]: {
|
||||||
|
type: string;
|
||||||
|
description?: string;
|
||||||
|
};
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
api.response.forEach((item) => {
|
||||||
|
properties[item.type] = {
|
||||||
|
type: item.key ?? item.type,
|
||||||
|
description: item.comment
|
||||||
|
};
|
||||||
|
});
|
||||||
|
const res: OpenAPIResponse = {
|
||||||
|
'200': {
|
||||||
|
description: api.description ?? '',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object',
|
||||||
|
properties
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'200': {
|
||||||
|
description: api.response.comment ?? '',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: api.response.type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'200': {
|
||||||
|
description: api.description ?? '',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: {
|
||||||
|
type: 'object'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return {
|
||||||
|
[method]: {
|
||||||
|
description: api.description ?? '',
|
||||||
|
parameters,
|
||||||
|
responses
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function convertOpenApi({
|
||||||
|
apis,
|
||||||
|
...rest
|
||||||
|
}: {
|
||||||
|
apis: ApiType[];
|
||||||
|
} & Omit<OpenApiType, 'paths'>): OpenApiType {
|
||||||
|
const paths: PathsType = {};
|
||||||
|
apis.forEach((api) => {
|
||||||
|
paths[api.url] = convertPath(api);
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
paths,
|
||||||
|
...rest
|
||||||
|
};
|
||||||
|
}
|
21
scripts/openapi/package.json
Normal file
21
scripts/openapi/package.json
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "test",
|
||||||
|
"module": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc index.ts"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/types": "^7.25.6",
|
||||||
|
"@types/babel__generator": "^7.6.8",
|
||||||
|
"@types/babel__traverse": "^7.20.6"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/generator": "^7.25.6",
|
||||||
|
"@babel/parser": "^7.25.6",
|
||||||
|
"@babel/traverse": "^7.25.6",
|
||||||
|
"babel": "^6.23.0"
|
||||||
|
}
|
||||||
|
}
|
40
scripts/openapi/template.md
Normal file
40
scripts/openapi/template.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
```ts
|
||||||
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
|
||||||
|
// This should be at the top of the file after the imports
|
||||||
|
export const ApiMetadata = {
|
||||||
|
name: 'template example api',
|
||||||
|
author: 'Finley',
|
||||||
|
version: '0.1.0',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TemplateQuery = {
|
||||||
|
// The App's ID
|
||||||
|
appId?: string[],
|
||||||
|
// The App's Name
|
||||||
|
name: string,
|
||||||
|
// The App's Description
|
||||||
|
description: string | Something<AppDetailType>,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TemplateBody = {
|
||||||
|
// The App's Name
|
||||||
|
name: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the response type for the API
|
||||||
|
export type TemplateResponse = AppDetailType;
|
||||||
|
|
||||||
|
// This is the template API for FASTGPT NextAPI
|
||||||
|
async function handler(
|
||||||
|
req: ApiRequestProps<TemplateBody, TemplateQuery>,
|
||||||
|
res: ApiResponseType<any>,
|
||||||
|
): Promise<TemplateResponse> {
|
||||||
|
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|
||||||
|
```
|
22
scripts/openapi/type.d.ts
vendored
Normal file
22
scripts/openapi/type.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
export type ApiMetaData = {
|
||||||
|
name?: string;
|
||||||
|
author?: string;
|
||||||
|
version?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ApiType = {
|
||||||
|
description?: string;
|
||||||
|
path: string;
|
||||||
|
url: string;
|
||||||
|
query?: itemType | itemType[];
|
||||||
|
body?: itemType | itemType[];
|
||||||
|
response?: itemType | itemType[];
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||||
|
} & ApiMetaData;
|
||||||
|
|
||||||
|
export type itemType = {
|
||||||
|
comment?: string;
|
||||||
|
key?: string;
|
||||||
|
type: string;
|
||||||
|
required?: boolean;
|
||||||
|
};
|
223
scripts/openapi/utils.ts
Normal file
223
scripts/openapi/utils.ts
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
import type { TSType, TSTypeLiteral } from '@babel/types';
|
||||||
|
import { parse } from '@babel/parser';
|
||||||
|
import traverse, { NodePath } from '@babel/traverse';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import type { ApiMetaData, ApiType, itemType } from './type';
|
||||||
|
|
||||||
|
function getMetadata(path: NodePath): ApiMetaData | undefined {
|
||||||
|
const metadata = {
|
||||||
|
name: '',
|
||||||
|
author: '',
|
||||||
|
version: '',
|
||||||
|
method: ''
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
path.isExportNamedDeclaration() && // get metadata
|
||||||
|
path.node.declaration?.type === 'VariableDeclaration' &&
|
||||||
|
path.node.declaration.declarations[0]?.id.type === 'Identifier' &&
|
||||||
|
path.node.declaration.declarations[0].id.name === 'ApiMetadata' &&
|
||||||
|
path.node.declaration.declarations[0].init?.type === 'ObjectExpression'
|
||||||
|
) {
|
||||||
|
path.node.declaration.declarations[0].init.properties.forEach((item) => {
|
||||||
|
if (item.type === 'ObjectProperty') {
|
||||||
|
const key = item.key.type === 'Identifier' ? item.key.name : item.key.type;
|
||||||
|
if (key === 'name') {
|
||||||
|
metadata.name = item.value.type === 'StringLiteral' ? item.value.value : item.value.type;
|
||||||
|
}
|
||||||
|
if (key === 'author') {
|
||||||
|
metadata.author =
|
||||||
|
item.value.type === 'StringLiteral' ? item.value.value : item.value.type;
|
||||||
|
}
|
||||||
|
if (key === 'version') {
|
||||||
|
metadata.version =
|
||||||
|
item.value.type === 'StringLiteral' ? item.value.value : item.value.type;
|
||||||
|
} else if (key === 'method') {
|
||||||
|
metadata.method =
|
||||||
|
item.value.type === 'StringLiteral' ? item.value.value : item.value.type;
|
||||||
|
metadata.method = metadata.method.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (metadata.name && metadata.author && metadata.version) {
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDescription(path: NodePath) {
|
||||||
|
if (path.isFunctionDeclaration() && path.node.id?.name === 'handler') {
|
||||||
|
const comments = path.node.leadingComments?.map((item) => item.value.trim()).join('\n');
|
||||||
|
return comments;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ApiDataType = {
|
||||||
|
type?: 'query' | 'body' | 'response';
|
||||||
|
comment?: string;
|
||||||
|
items?: itemType[];
|
||||||
|
dataType?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parseType(type?: TSType): string {
|
||||||
|
if (!type) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
if (type.type === 'TSTypeReference') {
|
||||||
|
return type.typeName.type === 'Identifier' ? type.typeName.name : type.typeName.type;
|
||||||
|
} else if (type.type === 'TSArrayType') {
|
||||||
|
return `${parseType(type.elementType)}[]`;
|
||||||
|
} else if (type.type === 'TSUnionType') {
|
||||||
|
return type.types.map((item) => parseType(item)).join(' | ');
|
||||||
|
} else if (type.type === 'TSIntersectionType') {
|
||||||
|
return type.types.map((item) => parseType(item)).join(' & ');
|
||||||
|
} else if (type.type === 'TSLiteralType') {
|
||||||
|
return type.literal.type === 'StringLiteral' ? type.literal.value : type.literal.type;
|
||||||
|
// } else if (type.type === 'TSTypeLiteral') {
|
||||||
|
// return parseTypeLiteral(type);
|
||||||
|
} else if (type.type === 'TSStringKeyword') {
|
||||||
|
return 'string';
|
||||||
|
} else if (type.type === 'TSNumberKeyword') {
|
||||||
|
return 'number';
|
||||||
|
} else if (type.type === 'TSBooleanKeyword') {
|
||||||
|
return 'boolean';
|
||||||
|
} else {
|
||||||
|
return type.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseTypeLiteral(type: TSTypeLiteral): itemType[] {
|
||||||
|
const items: itemType[] = [];
|
||||||
|
type.members.forEach((item) => {
|
||||||
|
if (item.type === 'TSPropertySignature') {
|
||||||
|
const key = item.key.type === 'Identifier' ? item.key.name : item.key.type;
|
||||||
|
const value = parseType(item.typeAnnotation?.typeAnnotation);
|
||||||
|
const comments = [
|
||||||
|
item.leadingComments?.map((item) => item.value.trim()).join('\n'),
|
||||||
|
item.trailingComments?.map((item) => item.value.trim()).join('\n')
|
||||||
|
].join('\n');
|
||||||
|
const required = item.optional ? false : true;
|
||||||
|
items.push({
|
||||||
|
type: value,
|
||||||
|
comment: comments,
|
||||||
|
key,
|
||||||
|
required
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getData(path: NodePath): ApiDataType | undefined {
|
||||||
|
const type: ApiDataType = {};
|
||||||
|
if (path.isExportNamedDeclaration()) {
|
||||||
|
const comments = [
|
||||||
|
path.node.leadingComments?.map((item) => item.value.trim()).join('\n'),
|
||||||
|
path.node.trailingComments?.map((item) => item.value.trim()).join('\n')
|
||||||
|
].join('\n');
|
||||||
|
if (comments) {
|
||||||
|
type.comment = comments;
|
||||||
|
}
|
||||||
|
if (path.node.declaration?.type === 'TSTypeAliasDeclaration') {
|
||||||
|
if (path.node.declaration.id.type === 'Identifier') {
|
||||||
|
if (path.node.declaration.id.name.endsWith('Query')) {
|
||||||
|
type.type = 'query';
|
||||||
|
const queryType = path.node.declaration.typeAnnotation;
|
||||||
|
if (queryType) {
|
||||||
|
if (queryType.type === 'TSTypeLiteral') {
|
||||||
|
type.items = parseTypeLiteral(queryType);
|
||||||
|
} else {
|
||||||
|
type.dataType = parseType(queryType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (path.node.declaration.id.name.endsWith('Body')) {
|
||||||
|
type.type = 'body';
|
||||||
|
if (path.node.declaration.typeAnnotation) {
|
||||||
|
if (path.node.declaration.typeAnnotation.type === 'TSTypeLiteral') {
|
||||||
|
type.items = parseTypeLiteral(path.node.declaration.typeAnnotation);
|
||||||
|
} else {
|
||||||
|
type.dataType = parseType(path.node.declaration.typeAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (path.node.declaration.id.name.endsWith('Response')) {
|
||||||
|
type.type = 'response';
|
||||||
|
if (path.node.declaration.typeAnnotation) {
|
||||||
|
if (path.node.declaration.typeAnnotation.type === 'TSTypeLiteral') {
|
||||||
|
type.items = parseTypeLiteral(path.node.declaration.typeAnnotation);
|
||||||
|
} else {
|
||||||
|
type.dataType = parseType(path.node.declaration.typeAnnotation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCode(code: string): ApiType {
|
||||||
|
const ast = parse(code, {
|
||||||
|
sourceType: 'module',
|
||||||
|
plugins: ['typescript', 'jsx']
|
||||||
|
});
|
||||||
|
|
||||||
|
const api = <ApiType>{};
|
||||||
|
|
||||||
|
traverse(ast, {
|
||||||
|
enter(path) {
|
||||||
|
const metadata = getMetadata(path);
|
||||||
|
const description = getDescription(path);
|
||||||
|
const data = getData(path);
|
||||||
|
if (metadata) {
|
||||||
|
api.name = metadata.name;
|
||||||
|
api.author = metadata.author;
|
||||||
|
api.version = metadata.version;
|
||||||
|
}
|
||||||
|
if (description) {
|
||||||
|
api.description = description;
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
if (data.type === 'query') {
|
||||||
|
api.query = data.items ?? {
|
||||||
|
type: data.dataType ?? '',
|
||||||
|
comment: data.comment ?? ''
|
||||||
|
};
|
||||||
|
} else if (data.type === 'body') {
|
||||||
|
api.body = data.items ?? {
|
||||||
|
type: data.dataType ?? '',
|
||||||
|
comment: data.comment ?? ''
|
||||||
|
};
|
||||||
|
} else if (data.type === 'response') {
|
||||||
|
api.response = data.items ?? {
|
||||||
|
type: data.dataType ?? '',
|
||||||
|
comment: data.comment ?? ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMethod(api: ApiType): 'GET' | 'POST' {
|
||||||
|
if (api.query && !(Array.isArray(api.query) && api.query.length === 0)) {
|
||||||
|
return 'GET';
|
||||||
|
} else if (api.body && !(Array.isArray(api.body) && api.body.length === 0)) {
|
||||||
|
return 'POST';
|
||||||
|
} else {
|
||||||
|
return 'GET';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseAPI({ path, rootPath }: { path: string; rootPath: string }): ApiType {
|
||||||
|
const code = fs.readFileSync(path, 'utf-8');
|
||||||
|
const api = parseCode(code);
|
||||||
|
api.url = path.replace('.ts', '').replace(rootPath, '');
|
||||||
|
api.path = path;
|
||||||
|
if (api.method === undefined) {
|
||||||
|
api.method = getMethod(api);
|
||||||
|
}
|
||||||
|
return api;
|
||||||
|
}
|
Reference in New Issue
Block a user