@1szx1/vite-plugin-swagger2ts 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +231528 -0
- package/dist/index.mjs +231528 -0
- package/index.d.ts +4 -0
- package/package.json +89 -0
- package/src/generator.ts +176 -0
- package/src/global.d.ts +7 -0
- package/src/index.ts +57 -0
- package/src/tmpl.ts +52 -0
- package/src/types.ts +43 -0
- package/src/utils.ts +83 -0
package/index.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@1szx1/vite-plugin-swagger2ts",
|
|
3
|
+
"description": "fork for https://github.com/1SZX1/vite-plugin-swagger2ts",
|
|
4
|
+
"version": "1.0.4",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "index.d.ts",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"author": "1SZX1 <2414126198@qq.com>",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/1SZX1/vite-plugin-swagger2ts"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/1SZX1/vite-plugin-swagger2ts",
|
|
15
|
+
"bugs": "https://github.com/1SZX1/vite-plugin-swagger2ts/issues",
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"src",
|
|
19
|
+
"index.d.ts"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"dev": "pnpm run build -- --watch --ignore-watch examples",
|
|
23
|
+
"build": "tsup src/index.ts --dts --format cjs,esm",
|
|
24
|
+
"prepublishOnly": "pnpm run build",
|
|
25
|
+
"release": "pnpx git-ensure -a && pnpx bumpp --commit --tag --push",
|
|
26
|
+
"demo:dev": "pnpm -C examples/demo run dev",
|
|
27
|
+
"demo:build": "pnpm -C examples/demo run build",
|
|
28
|
+
"demo:preview": "pnpm -C examples/demo run preview",
|
|
29
|
+
"test": "jest",
|
|
30
|
+
"lint": "eslint --fix --ext .ts ./",
|
|
31
|
+
"prepare": "[ -d '.husky' ] && echo '' || (pnpx husky install && pnpm commitprepare && pnpm lintprepare)",
|
|
32
|
+
"commitprepare": "pnpx husky add .husky/commit-msg 'pnpx commitlint --edit $1'",
|
|
33
|
+
"lintprepare": "pnpx husky add .husky/pre-commit 'pnpx lint-staged'",
|
|
34
|
+
"version": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0 && git add CHANGELOG.md",
|
|
35
|
+
"preinstall": "npx only-allow pnpm"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"cross-fetch": "^3.1.5",
|
|
39
|
+
"openapi3-ts": "^2.0.2",
|
|
40
|
+
"swagger2openapi": "^7.0.8"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"vite": "^2.0.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@antfu/eslint-config": "^0.6.6",
|
|
47
|
+
"@commitlint/cli": "^12.1.4",
|
|
48
|
+
"@commitlint/config-conventional": "^12.1.4",
|
|
49
|
+
"@types/jest": "^26.0.24",
|
|
50
|
+
"@types/node": "^14.18.12",
|
|
51
|
+
"@typescript-eslint/eslint-plugin": "^4.33.0",
|
|
52
|
+
"@typescript-eslint/parser": "^5.14.0",
|
|
53
|
+
"conventional-changelog": "^3.1.25",
|
|
54
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
55
|
+
"eslint": "^7.32.0",
|
|
56
|
+
"eslint-config-prettier": "^8.5.0",
|
|
57
|
+
"eslint-plugin-prettier": "^3.4.1",
|
|
58
|
+
"husky": "^6.0.0",
|
|
59
|
+
"jest": "^26.6.3",
|
|
60
|
+
"jest-esbuild": "^0.1.6",
|
|
61
|
+
"lint-staged": "^10.5.4",
|
|
62
|
+
"prettier": "^2.5.1",
|
|
63
|
+
"rollup": "^2.70.0",
|
|
64
|
+
"ts-node": "^9.1.1",
|
|
65
|
+
"tsup": "^4.14.0",
|
|
66
|
+
"typescript": "^4.6.2",
|
|
67
|
+
"vite": "^2.8.6"
|
|
68
|
+
},
|
|
69
|
+
"lint-staged": {
|
|
70
|
+
"*.{ts}": "eslint --fix --ext",
|
|
71
|
+
"*.{ts,md}": "prettier --write"
|
|
72
|
+
},
|
|
73
|
+
"husky": {
|
|
74
|
+
"hooks": {
|
|
75
|
+
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
|
|
76
|
+
"pre-commit": "lint-staged"
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"commitlint": {
|
|
80
|
+
"extends": [
|
|
81
|
+
"@commitlint/config-conventional"
|
|
82
|
+
]
|
|
83
|
+
},
|
|
84
|
+
"config": {
|
|
85
|
+
"commitizen": {
|
|
86
|
+
"path": "./node_modules/cz-conventional-changelog"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
package/src/generator.ts
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { OpenAPIObject, RequestBodyObject, ResponseObject, ReferenceObject, OperationObject, ParameterObject, SchemaObject } from "openapi3-ts";
|
|
2
|
+
import { getRefName, isTypeAny } from "./utils";
|
|
3
|
+
import { GenerateDocOption } from "./types";
|
|
4
|
+
|
|
5
|
+
export function generateDocs(input: OpenAPIObject, { docsName, baseUrl, formatSchema }: GenerateDocOption) {
|
|
6
|
+
function getRefObject(ref: string) {
|
|
7
|
+
return ref
|
|
8
|
+
.split("/")
|
|
9
|
+
.slice(1)
|
|
10
|
+
.reduce((obj, item) => {
|
|
11
|
+
return obj[item] || {};
|
|
12
|
+
}, input);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const pathsTypeString = Object.entries(input.paths)
|
|
16
|
+
.map(([endPoint, value]) => {
|
|
17
|
+
const pathValue = value as OperationObject;
|
|
18
|
+
const parametersExtended = pathValue.parameters;
|
|
19
|
+
// 处理请求方法里面的类型
|
|
20
|
+
const result = Object.entries(pathValue)
|
|
21
|
+
.map(([method, options]: [string, OperationObject]) => {
|
|
22
|
+
// 不是请求方法的属性都跳过
|
|
23
|
+
if (!["get", "put", "post", "delete", "options", "head", "patch", "trace"].includes(method)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let allParameters = [...(parametersExtended || []), ...(options.parameters || [])].map((item) => {
|
|
27
|
+
// 将 $ref 转换为正常的 ParameterObject 的对象
|
|
28
|
+
if ("$ref" in item) {
|
|
29
|
+
return getRefObject(item.$ref) as any as ParameterObject;
|
|
30
|
+
} else {
|
|
31
|
+
return item;
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const queryParameters = allParameters.filter((item) => item.in === "query");
|
|
35
|
+
const pathParameters = allParameters.filter((item) => item.in === "path");
|
|
36
|
+
const bodyParameters = allParameters.filter((item) => !item.in);
|
|
37
|
+
|
|
38
|
+
// 将 $ref 转换为 requestBody 对象
|
|
39
|
+
const requestBody = options.requestBody ? ("$ref" in options.requestBody ? (getRefObject(options.requestBody.$ref) as any as RequestBodyObject) : options.requestBody) : undefined;
|
|
40
|
+
|
|
41
|
+
// 找到状态码为 200 或者 default 的 responses 体,并将 ReferenceObject 格式的转换为 ResponseObject;
|
|
42
|
+
let responses = Object.entries(options.responses)
|
|
43
|
+
.filter((items) => items[0] == "200" || items[0] == "default")
|
|
44
|
+
.sort()?.[0]?.[1] as ResponseObject | ReferenceObject;
|
|
45
|
+
|
|
46
|
+
if ("$ref" in responses) {
|
|
47
|
+
responses = getRefObject(responses.$ref) as any as ResponseObject;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const queryString = queryParameters.length > 0 ? jsDoc({}, { key: "query", result: `{${queryParameters.map((item) => generatorParameterType(item, docsName)).join("")}\n}` }) : "";
|
|
51
|
+
const pathString = pathParameters.length > 0 ? jsDoc({}, { key: "path", result: `{${pathParameters.map((item) => generatorParameterType(item, docsName)).join("")}\n}` }) : "";
|
|
52
|
+
const bodyExtraString = bodyParameters.length > 0 ? jsDoc({}, { key: "body", result: `{${bodyParameters.map((item) => generatorParameterType(item, docsName)).join("")}\n}` }) : "";
|
|
53
|
+
const bodyString = requestBody ? generatorRequestBodyType(requestBody, docsName) : "";
|
|
54
|
+
const responseString = generatorResponseBodyType(responses, docsName);
|
|
55
|
+
|
|
56
|
+
const paramString = `${queryString}${pathString}${bodyString || bodyExtraString}`;
|
|
57
|
+
return jsDoc({}, { key: method, result: `{${paramString ? `\nparam:{${paramString}}` : ""}${responseString}}` });
|
|
58
|
+
})
|
|
59
|
+
.join("");
|
|
60
|
+
|
|
61
|
+
return jsDoc({}, { key: `'${baseUrl}${endPoint}'`, result: `{${result}}` });
|
|
62
|
+
})
|
|
63
|
+
.join("");
|
|
64
|
+
const typeString = input.components?.schemas
|
|
65
|
+
? Object.entries(input.components.schemas)
|
|
66
|
+
.map(([name, schema]) => {
|
|
67
|
+
if (typeof formatSchema == "function") {
|
|
68
|
+
schema = formatSchema(schema);
|
|
69
|
+
}
|
|
70
|
+
if ("$ref" in schema) {
|
|
71
|
+
schema = getRefObject(schema.$ref) as any as SchemaObject;
|
|
72
|
+
}
|
|
73
|
+
return generatorSchemaType(getRefName(`#/components/schemas/${name}`, docsName), schema, { isRoot: true, isExport: true, docsName });
|
|
74
|
+
})
|
|
75
|
+
.join("")
|
|
76
|
+
: "";
|
|
77
|
+
const apiString = `${typeString}\nexport interface Paths${getRefName(docsName)}{${pathsTypeString}}\n`;
|
|
78
|
+
return apiString.replaceAll(/\n\n/g, "\n").replaceAll(/\n\n/g, "\n");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const TYPES = {
|
|
82
|
+
integer: "number",
|
|
83
|
+
number: "number",
|
|
84
|
+
boolean: "boolean",
|
|
85
|
+
object: "object",
|
|
86
|
+
string: "string",
|
|
87
|
+
array: "array"
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
interface GeneratorSchemaTypeOption {
|
|
91
|
+
isRoot?: boolean;
|
|
92
|
+
isRequired?: boolean;
|
|
93
|
+
isExport?: boolean;
|
|
94
|
+
docsName?: string;
|
|
95
|
+
}
|
|
96
|
+
export function generatorSchemaType(
|
|
97
|
+
key: string,
|
|
98
|
+
schema: SchemaObject | boolean | {} | undefined,
|
|
99
|
+
{ isRoot = false, isRequired, isExport = false, docsName = "" }: GeneratorSchemaTypeOption = {}
|
|
100
|
+
): string {
|
|
101
|
+
if (isTypeAny(schema)) {
|
|
102
|
+
return `\n${key}${isRequired === false ? "?" : ""}: any;\n`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { type, $ref, enum: Enum, items, properties, oneOf, additionalProperties, required, allOf } = schema as SchemaObject;
|
|
106
|
+
let result = "";
|
|
107
|
+
if ($ref) {
|
|
108
|
+
// 引用
|
|
109
|
+
result = getRefName($ref, docsName);
|
|
110
|
+
} else if (Enum) {
|
|
111
|
+
// 枚举
|
|
112
|
+
result = `${Enum.map((t) => `"${t}"`).join(" | ")}`;
|
|
113
|
+
} else if (items) {
|
|
114
|
+
// 数组
|
|
115
|
+
result = `${generatorSchemaType("", items, { docsName })}[]`;
|
|
116
|
+
} else if (properties) {
|
|
117
|
+
// 对象
|
|
118
|
+
result += `{${Object.entries(properties)
|
|
119
|
+
.map(([pName, _schema]) => generatorSchemaType(pName, _schema, { isRequired: !required || required.includes(pName) ? true : false, docsName }))
|
|
120
|
+
.join("")}\n}`;
|
|
121
|
+
} else if (oneOf) {
|
|
122
|
+
// 联合类型
|
|
123
|
+
result = `${oneOf.map((_schema) => `${generatorSchemaType("", _schema, { docsName })}`).join(" | ")}`;
|
|
124
|
+
} else if (allOf) {
|
|
125
|
+
// 交叉类型
|
|
126
|
+
result = `${allOf.map((_schema) => generatorSchemaType("", _schema, { docsName })).join(" & ")}`;
|
|
127
|
+
} else if (type === "object" && !result) {
|
|
128
|
+
// additionalProperties
|
|
129
|
+
if (additionalProperties) {
|
|
130
|
+
result = `{${generatorSchemaType("[x: string | number]", additionalProperties, { docsName })}\n}`;
|
|
131
|
+
} else {
|
|
132
|
+
result = "{[x: string | number]: any}";
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
// 基本类型,如果没有在基础类型里面默认为 string
|
|
136
|
+
result = TYPES[type as keyof typeof TYPES] || "string";
|
|
137
|
+
}
|
|
138
|
+
if (!key) {
|
|
139
|
+
return result;
|
|
140
|
+
}
|
|
141
|
+
return jsDoc(schema, { key, result, isExport, isRequired, isRoot });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function generatorParameterType(schemas: ParameterObject, docsName?: string): string {
|
|
145
|
+
const { name, required, schema, description } = schemas;
|
|
146
|
+
const result = generatorSchemaType("", schema, { docsName });
|
|
147
|
+
return jsDoc({ description, ...schema }, { key: name, result, isRequired: required });
|
|
148
|
+
}
|
|
149
|
+
export function generatorRequestBodyType(schemas: RequestBodyObject, docsName?: string): string {
|
|
150
|
+
const { description, content, required } = schemas;
|
|
151
|
+
const schema = content && Object.values(content)[0]?.["schema"];
|
|
152
|
+
if (!schema) return "";
|
|
153
|
+
const result = generatorSchemaType("", schema, { docsName });
|
|
154
|
+
return jsDoc({ description }, { key: "body", result, isRequired: required });
|
|
155
|
+
}
|
|
156
|
+
export function generatorResponseBodyType(schemas: ResponseObject, docsName?: string): string {
|
|
157
|
+
const { description, content } = schemas;
|
|
158
|
+
const schema = content && Object.values(content)[0]?.["schema"];
|
|
159
|
+
let result: string;
|
|
160
|
+
if (!schema) {
|
|
161
|
+
result = "any";
|
|
162
|
+
} else {
|
|
163
|
+
result = generatorSchemaType("", schema, { docsName });
|
|
164
|
+
}
|
|
165
|
+
return jsDoc({ description }, { key: "response", result });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function jsDoc(
|
|
169
|
+
{ description, title, format, example, default: default1 }: any,
|
|
170
|
+
{ key, result, isRoot, isExport, isRequired }: { key: string; result: string; isRoot?: boolean; isExport?: boolean; isRequired?: boolean }
|
|
171
|
+
): string {
|
|
172
|
+
const jsDoc = `\n/**\n${title ? ` * @title ${title}\n` : ""}${description ? ` * @description ${description}\n` : ""}${format ? ` * @format ${format}\n` : ""}${
|
|
173
|
+
default1 ? ` * @default ${default1}\n` : ""
|
|
174
|
+
}${example ? ` * @example ${example}\n` : ""} **/\n`;
|
|
175
|
+
return `${jsDoc == "\n/**\n **/\n" ? "" : jsDoc}${isRoot ? `${isExport ? "export " : ""}type ${key} = ${result};\n` : `${key}${isRequired === false ? "?" : ""}: ${result};\n`}`;
|
|
176
|
+
}
|
package/src/global.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
declare module "swagger2openapi" {
|
|
2
|
+
import { OpenAPIObject } from "openapi3-ts";
|
|
3
|
+
interface ConverObjCallbackData {
|
|
4
|
+
openapi: OpenAPIObject;
|
|
5
|
+
}
|
|
6
|
+
function convertObj(schema: unknown, options: {}, callback: (err: Error, data: ConverObjCallbackData) => void): void;
|
|
7
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { writeFileSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { convertObj } from "swagger2openapi";
|
|
4
|
+
import { OpenAPIObject } from "openapi3-ts";
|
|
5
|
+
import { doc, format } from "prettier";
|
|
6
|
+
import { UserOptions, ExportPlugin, SwaggerSource, SwaggerDoc } from "./types";
|
|
7
|
+
import { resolveOptions, fetchUrl, getPrettierOptions } from "./utils";
|
|
8
|
+
import { generateDocs } from "./generator";
|
|
9
|
+
|
|
10
|
+
function convertObjPromise(docs: any): Promise<OpenAPIObject> {
|
|
11
|
+
return new Promise((resolve, reject) => {
|
|
12
|
+
convertObj(docs, { patch: true }, function (err, option) {
|
|
13
|
+
// err && console.log("err", err);
|
|
14
|
+
if (err || !option.openapi) return reject(err);
|
|
15
|
+
resolve(option.openapi);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function swagger2TsPlugin(userOptions: UserOptions): ExportPlugin {
|
|
21
|
+
const { swaggerUrl, output, prettierPath, formatDocs, formatSchema } = resolveOptions(userOptions);
|
|
22
|
+
const prettierOptions = getPrettierOptions(prettierPath);
|
|
23
|
+
|
|
24
|
+
async function loadSwaggerSource() {
|
|
25
|
+
const sources = (await fetchUrl(`${swaggerUrl}/swagger-resources`)) as SwaggerSource[];
|
|
26
|
+
let code = "";
|
|
27
|
+
for (let i = 0; i < sources.length; i++) {
|
|
28
|
+
const { url, name: docsName } = sources[i];
|
|
29
|
+
try {
|
|
30
|
+
const docs = (await fetchUrl(`${swaggerUrl}${url}`)) as SwaggerDoc | OpenAPIObject;
|
|
31
|
+
let openapiDocs = formatDocs ? formatDocs(docs) : docs;
|
|
32
|
+
if (!("openapi" in openapiDocs)) {
|
|
33
|
+
if (docs.swagger) openapiDocs = await convertObjPromise(docs);
|
|
34
|
+
else continue;
|
|
35
|
+
}
|
|
36
|
+
const apistrings = generateDocs(openapiDocs, { docsName, baseUrl: docs.basePath ?? "", formatSchema });
|
|
37
|
+
code += apistrings;
|
|
38
|
+
// console.log("apistrings", apistrings);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// console.log("vite-plugin-swagger2ts convert error", error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const outputFile = resolve(process.cwd(), output);
|
|
44
|
+
writeFileSync(outputFile, format(code, prettierOptions));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
loadSwaggerSource();
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
name: "vite-plugin-swagger2ts",
|
|
51
|
+
enforce: "pre",
|
|
52
|
+
apply: "serve"
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export * from "./types";
|
|
57
|
+
export default swagger2TsPlugin;
|
package/src/tmpl.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
interface SwaggerInterface1 {
|
|
2
|
+
// SwaggerInterface start
|
|
3
|
+
adsf: {
|
|
4
|
+
get: {
|
|
5
|
+
param: {
|
|
6
|
+
path: { id: string };
|
|
7
|
+
};
|
|
8
|
+
response: any;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
adsf2: {
|
|
12
|
+
get: {
|
|
13
|
+
param: {
|
|
14
|
+
path: { id: string };
|
|
15
|
+
};
|
|
16
|
+
response: any;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
interface SwaggerInterface2 {
|
|
21
|
+
// SwaggerInterface start
|
|
22
|
+
ccccc1: {
|
|
23
|
+
get: {
|
|
24
|
+
param: {
|
|
25
|
+
path: { id: string };
|
|
26
|
+
};
|
|
27
|
+
response: any;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
ccccc2: {
|
|
31
|
+
get: {
|
|
32
|
+
param: {
|
|
33
|
+
path: { id: string };
|
|
34
|
+
};
|
|
35
|
+
response: any;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
type SwaggerInterface = SwaggerInterface1 & SwaggerInterface2;
|
|
41
|
+
type ReturnDataType = { code: number; data: any; message: string };
|
|
42
|
+
type ReturnData<T extends ReturnDataType | any> = T extends ReturnDataType ? T["data"] : T;
|
|
43
|
+
|
|
44
|
+
export type UrlKey = keyof SwaggerInterface;
|
|
45
|
+
export type MethodKey<U extends UrlKey> = string & keyof SwaggerInterface[U];
|
|
46
|
+
|
|
47
|
+
type SwaggerInterfaceSingle<U extends UrlKey, M extends MethodKey<U>> = SwaggerInterface[U][M];
|
|
48
|
+
type SwaggerField<U extends UrlKey, M extends MethodKey<U>> = keyof SwaggerInterfaceSingle<U, M>;
|
|
49
|
+
type SwaggerFieldType<U extends UrlKey, M extends MethodKey<U>, F extends SwaggerField<U, M>> = SwaggerInterfaceSingle<U, M>[F];
|
|
50
|
+
|
|
51
|
+
export type Param<U extends UrlKey, M extends MethodKey<U>> = SwaggerFieldType<U, M, "param" & SwaggerField<U, M>>;
|
|
52
|
+
export type Response<U extends UrlKey, M extends MethodKey<U>> = ReturnData<SwaggerFieldType<U, M, "response" & SwaggerField<U, M>>>;
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { OpenAPIObject, SchemaObject } from "openapi3-ts";
|
|
2
|
+
import type { Plugin } from "vite";
|
|
3
|
+
|
|
4
|
+
type FormatSchema = (schema: SchemaObject) => SchemaObject;
|
|
5
|
+
|
|
6
|
+
interface Options {
|
|
7
|
+
swaggerUrl: string;
|
|
8
|
+
output?: string;
|
|
9
|
+
prettierPath?: string;
|
|
10
|
+
formatDocs?: (docs: SwaggerDoc | OpenAPIObject) => typeof docs;
|
|
11
|
+
formatSchema?: FormatSchema;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type UserOptions = Options;
|
|
15
|
+
export interface ResolvedOptions extends Options {
|
|
16
|
+
output: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ExportPlugin = Required<Pick<Plugin, "name" | "enforce" | "apply">>;
|
|
20
|
+
|
|
21
|
+
export interface SwaggerSource {
|
|
22
|
+
name: string;
|
|
23
|
+
url: string;
|
|
24
|
+
swaggerVersion: string;
|
|
25
|
+
location: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SwaggerDoc {
|
|
29
|
+
basePath: string;
|
|
30
|
+
swagger: string;
|
|
31
|
+
paths: { [key: string]: any };
|
|
32
|
+
definitions: { [key: string]: any };
|
|
33
|
+
host: string;
|
|
34
|
+
info: any;
|
|
35
|
+
tags: any[];
|
|
36
|
+
securityDefinitions: any;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface GenerateDocOption {
|
|
40
|
+
docsName: string;
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
formatSchema?: FormatSchema;
|
|
43
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
import { UserOptions, ResolvedOptions } from "./types";
|
|
4
|
+
import { OpenAPIObject, SchemaObject } from "openapi3-ts";
|
|
5
|
+
import fetch from "cross-fetch";
|
|
6
|
+
|
|
7
|
+
export function resolveOptions(userOptions: UserOptions): ResolvedOptions {
|
|
8
|
+
const { output } = userOptions;
|
|
9
|
+
|
|
10
|
+
const root = process.cwd();
|
|
11
|
+
const outputFile = output ? (output?.endsWith(".ts") ? output : output + ".ts") : "./src/swagger.ts";
|
|
12
|
+
|
|
13
|
+
return Object.assign({ output: resolve(root, outputFile) }, userOptions);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function fetchUrl(url: string) {
|
|
17
|
+
return await fetch(url).then((res) => res.json());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getRef(ref: string, apiDocs: OpenAPIObject) {
|
|
21
|
+
const paths = ref.split("/").slice(1);
|
|
22
|
+
return paths.reduce((obj: any, key) => {
|
|
23
|
+
return obj[key];
|
|
24
|
+
}, apiDocs) as SchemaObject;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isTypeAny(type: true | undefined | {} | SchemaObject) {
|
|
28
|
+
if (type === true) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (typeof type === "object" && Object.keys(type).length <= 0) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!type) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function toPascalCase(str: string): string {
|
|
44
|
+
return `${str.substring(0, 1).toUpperCase()}${str.substring(1)}`;
|
|
45
|
+
}
|
|
46
|
+
function replaceWithUpper(str: string, sp: string) {
|
|
47
|
+
let pointArray = str.split(sp);
|
|
48
|
+
pointArray = pointArray.map((point) => toPascalCase(point));
|
|
49
|
+
|
|
50
|
+
return pointArray.join("");
|
|
51
|
+
}
|
|
52
|
+
function getSchemaName(name: string): string {
|
|
53
|
+
const removePound = replaceWithUpper(name, "#");
|
|
54
|
+
const removeDot = replaceWithUpper(removePound, ".");
|
|
55
|
+
const removeBackTick = replaceWithUpper(removeDot, "`");
|
|
56
|
+
const removeFirstBracket = replaceWithUpper(removeBackTick, "[");
|
|
57
|
+
const removeLastBracket = replaceWithUpper(removeFirstBracket, "]");
|
|
58
|
+
const removeDash = replaceWithUpper(removeLastBracket, "-");
|
|
59
|
+
const removeSlash = replaceWithUpper(removeDash, "/");
|
|
60
|
+
return removeSlash;
|
|
61
|
+
}
|
|
62
|
+
export function getRefName($ref: string, prefix: string = ""): string {
|
|
63
|
+
return getSchemaName(prefix) + getSchemaName($ref || "");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getPrettierOptions(prettierPath?: string) {
|
|
67
|
+
let prettierOptions: any = {};
|
|
68
|
+
if (prettierPath && existsSync(prettierPath)) {
|
|
69
|
+
prettierOptions = JSON.parse(readFileSync(prettierPath).toString());
|
|
70
|
+
} else {
|
|
71
|
+
if (existsSync(".prettierrc")) {
|
|
72
|
+
prettierOptions = JSON.parse(readFileSync(".prettierrc").toString());
|
|
73
|
+
} else if (existsSync("prettier.json")) {
|
|
74
|
+
prettierOptions = JSON.parse(readFileSync("prettier.json").toString());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (!prettierOptions.parser) {
|
|
79
|
+
prettierOptions.parser = "typescript";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return prettierOptions;
|
|
83
|
+
}
|