Skip to content

Commit ac42fd0

Browse files
authored
Merge pull request #110 from long-woo/dev
v2.12.0
2 parents 6045899 + e3dc008 commit ac42fd0

File tree

13 files changed

+186
-68
lines changed

13 files changed

+186
-68
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ Create a `myPlugin.ts` file:
152152

153153
```ts
154154
// 引用模块
155-
// import { start } from 'https://deno.land/x/stc@2.11.0/mod.ts'
156-
import { start } from 'jsr:@lonu/stc@^2.11.0'
155+
// import { start } from 'https://deno.land/x/stc@2.12.0/mod.ts'
156+
import { start } from 'jsr:@lonu/stc@^2.12.0'
157157

158158
// Defining plugins
159159
const myPlugin: IPlugin = {

deno.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"name": "@lonu/stc",
3-
"version": "2.11.0",
3+
"version": "2.12.0",
44
"exports": "./mod.ts",
55
"tasks": {
66
"pack": "deno run -A src/pack.ts",
7-
"dev": "deno task pack && deno run -A --watch=src src/main.ts --url='https://petstore3.swagger.io/api/v3/openapi.json'",
7+
"dev": "deno task pack && deno run -A --watch=src src/main.ts --url='https://petstore3.swagger.io/api/v3/openapi.json' --clean=false",
88
"serve": "deno run -A --watch=src src/service.ts",
9-
"version": "echo '2.11.0' > release/version",
9+
"version": "echo '2.12.0' > release/version",
1010
"build:npm": "deno run -A src/npm/build.ts",
1111
"build:mac": "deno compile -A --target x86_64-apple-darwin --output release/stc src/main.ts",
1212
"build:mac-m": "deno compile -A --target aarch64-apple-darwin --output release/stc-m src/main.ts",
@@ -52,6 +52,7 @@
5252
"imports": {
5353
"./": "./",
5454
"/": "./",
55+
"x/": "https://deno.land/x/",
5556
"@deno-library/progress": "jsr:@deno-library/progress@^1.4.9",
5657
"@deno/dnt": "jsr:@deno/dnt@^0.41.3",
5758
"@eta-dev/eta": "jsr:@eta-dev/eta@^3.5.0",
@@ -60,6 +61,6 @@
6061
"@std/datetime": "jsr:@std/datetime@^0.225.1",
6162
"@std/fmt": "jsr:@std/fmt@^1.0.1",
6263
"@std/fs": "jsr:@std/fs@^1.0.2",
63-
"x/": "https://deno.land/x/"
64+
"diff": "npm:diff@^7.0.0"
6465
}
6566
}

src/app.ts

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import type { IPluginContext } from "./plugins/typeDeclaration.ts";
33
import Logs from "./console.ts";
44
import { PluginManager } from "./plugins/index.ts";
55
import { getApiPath, getDefinition } from "./core.ts";
6-
import { createFile, emptyDirectory, readFile, removeFile } from "./utils.ts";
6+
import { createDiffFile, createFile, readFile, removeFile } from "./utils.ts";
77
import { getT } from "./i18n/index.ts";
88

9+
const LOCK_FILE = ".stc.lock";
10+
911
/**
1012
* 初始化插件管理器
1113
*/
@@ -36,22 +38,49 @@ const createContext = (context: IPluginContext) => {
3638
* @param urlOrPath - 远程地址或本地
3739
* @returns
3840
*/
39-
const getData = async (urlOrPath: string): Promise<ISwaggerResult> => {
40-
if (!/^http(s?):\/\//.test(urlOrPath)) {
41-
const content = await readFile(urlOrPath);
42-
43-
try {
44-
return JSON.parse(content) as unknown as ISwaggerResult;
45-
} catch (error) {
46-
throw new Error(getT("$t(app.apiJsonFileError)", { error }));
47-
}
48-
}
41+
const getData = async (
42+
urlOrPath: string,
43+
_outDir: string,
44+
): Promise<ISwaggerResult> => {
45+
try {
46+
// const lockFile = await readFile(`${outDir}/${LOCK_FILE}`);
47+
let data: ISwaggerResult;
4948

50-
// 从远程地址获取 Swagger 数据
51-
const res = await fetch(urlOrPath);
49+
// 从本地文件获取 Swagger 数据
50+
if (!/^http(s?):\/\//.test(urlOrPath)) {
51+
const content = await readFile(urlOrPath);
5252

53-
try {
54-
const data = await res.json();
53+
data = JSON.parse(content) as unknown as ISwaggerResult;
54+
} else {
55+
// 从远程地址获取 Swagger 数据
56+
const res = await fetch(urlOrPath);
57+
58+
data = await res.json();
59+
}
60+
61+
// 对比 path 和 definitions/schemas 数据是否有变化,有变化则使用新数据
62+
// if (lockFile) {
63+
// const oldData = JSON.parse(lockFile) as unknown as ISwaggerResult;
64+
// createFile(
65+
// `${outDir}/.stc_new.lock`,
66+
// JSON.stringify(data, null, 2),
67+
// {
68+
// banner: false,
69+
// },
70+
// );
71+
// const isChange = diff["diffLines"](
72+
// JSON.stringify(oldData, null, 2),
73+
// JSON.stringify(data, null, 2),
74+
// );
75+
76+
// createFile(
77+
// `${outDir}/.stc_diff.lock`,
78+
// JSON.stringify(isChange, null, 2),
79+
// {
80+
// banner: false,
81+
// },
82+
// );
83+
// }
5584

5685
return data;
5786
} catch (error) {
@@ -67,7 +96,7 @@ export const start = async (options: DefaultConfigOptions): Promise<void> => {
6796
// 创建上下文
6897
const context = createContext({ options });
6998

70-
const data = await getData(options.url);
99+
const data = await getData(options.url, options.outDir);
71100

72101
// 触发插件 onload 事件
73102
context.onLoad?.(data, context.options);
@@ -81,12 +110,15 @@ export const start = async (options: DefaultConfigOptions): Promise<void> => {
81110
// 触发插件 onAction 事件
82111
context.onAction?.(actionData, context.options);
83112

84-
if (options.shared) {
85-
// 清空输出目录
86-
await emptyDirectory(options.outDir);
87-
} else {
113+
if (options.clean) {
114+
const exclude = [`${options.outDir}/${LOCK_FILE}`];
115+
116+
if (!options.shared) {
117+
exclude.push(`${options.outDir}/shared/**/*`);
118+
}
119+
88120
await removeFile(`${options.outDir}/**/*.*`, {
89-
exclude: [`${options.outDir}/shared/**/*`],
121+
exclude,
90122
});
91123
}
92124

@@ -99,22 +131,24 @@ export const start = async (options: DefaultConfigOptions): Promise<void> => {
99131

100132
// 写入类型定义文件
101133
if (transformData?.definition?.content) {
102-
createFile(
103-
`${options.outDir}/${transformData.definition.filename}`,
104-
transformData.definition.content,
105-
);
134+
const name = `${options.outDir}/${transformData.definition.filename}`;
135+
const content = transformData.definition.content;
136+
137+
createDiffFile(name, content, options.clean);
106138
}
107139

108140
// 写入 API 文件
109141
if (transformData?.action) {
110142
transformData.action.forEach((content, filename) => {
111-
createFile(
112-
`${options.outDir}/${filename}`,
113-
content,
114-
);
143+
createDiffFile(`${options.outDir}/${filename}`, content, options.clean);
115144
});
116145
}
117146

147+
// 保存数据
148+
createFile(`${options.outDir}/${LOCK_FILE}`, JSON.stringify(data, null, 2), {
149+
banner: false,
150+
});
151+
118152
console.log("\n");
119153
Logs.success(`${getT("$t(app.generateFileDone)")}\n\t${options.outDir}\n`);
120154
// 触发插件 onEnd 事件

src/cli.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,11 @@ const checkUpdate = async () => {
3838
const _lastVersion = Number(latestVersion.replace(/\./g, "") ?? 0);
3939

4040
if (version < _lastVersion) {
41+
// 非 deno compile 的可执行文件,仅提示更新
4142
if (typeof confirm === "undefined") {
42-
// 提示用户是否更新
43+
// 提示有新版本
4344
console.log("\n");
44-
Logs.info(getT("$t(cli.updatePrompt)", {
45+
Logs.warn(getT("$t(cli.updatePrompt)", {
4546
version: denoJson.version,
4647
latestVersion,
4748
}));
@@ -173,6 +174,7 @@ ${getT("$t(cli.option)")}
173174
-c, --conjunction ${getT("$t(cli.option_conjunction)")}
174175
--actionIndex ${getT("$t(cli.option_actionIndex)")}
175176
--shared ${getT("$t(cli.option_shared)")}
177+
--clean ${getT("$t(cli.option_clean)")}
176178
-v, --version ${getT("$t(cli.option_version)")}
177179
178180
${getT("$t(cli.example)")}
@@ -188,7 +190,7 @@ ${getT("$t(cli.example)")}
188190
export const main = async (): Promise<DefaultConfigOptions> => {
189191
// 定义命令行参数和选项的配置
190192
const argsConfig: ParseOptions = {
191-
boolean: ["help", "version", "shared"],
193+
boolean: ["help", "version", "shared", "clean"],
192194
string: [
193195
"url",
194196
"outDir",
@@ -215,6 +217,7 @@ export const main = async (): Promise<DefaultConfigOptions> => {
215217
conjunction: "By",
216218
actionIndex: "-1",
217219
shared: true,
220+
clean: true,
218221
},
219222
unknown: (arg: string) => {
220223
Logs.error(getT("$t(cli.unknownOption)", { arg }));
@@ -259,5 +262,6 @@ export const main = async (): Promise<DefaultConfigOptions> => {
259262
conjunction: args.conjunction,
260263
actionIndex: args.actionIndex,
261264
shared: args.shared,
265+
clean: args.clean,
262266
};
263267
};

src/core.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -384,17 +384,21 @@ const getPathVirtualProperty = (
384384
const _bodyContentRef = getRefType(
385385
_bodyContentSchema?.$ref ?? _bodyContentSchema?.items?.$ref ?? "",
386386
);
387-
const _name = (_key === "application/octet-stream"
388-
? "file"
389-
: lowerCase(_bodyContentRef)) || "body";
390387

391388
// 处理 type 为 object 的情况,并且有 properties 属性
392389
if (
393390
_bodyContentSchema?.type === "object" &&
394391
!Object.keys(_bodyContentSchema?.properties ?? {}).length
395-
) {
396-
return;
397-
}
392+
) return;
393+
394+
const _name =
395+
(["application/octet-stream", "multipart/form-data"].includes(_key)
396+
? "file"
397+
: lowerCase(_bodyContentRef)) || "body";
398+
399+
const _type = _name === "file"
400+
? "FormData"
401+
: _bodyContentSchema?.type ?? "";
398402

399403
const _properties = getProperties(
400404
_bodyContentSchema?.properties ?? {},
@@ -403,7 +407,7 @@ const getPathVirtualProperty = (
403407

404408
const _body: IDefinitionVirtualProperty = {
405409
name: _name,
406-
type: _bodyContentSchema?.type ?? "",
410+
type: _type,
407411
required: _requestBody.required ?? true,
408412
description: _requestBody.description,
409413
ref: _bodyContentRef,
@@ -412,9 +416,7 @@ const getPathVirtualProperty = (
412416

413417
// body 存在相同 name 时,无需重复添加
414418
if (
415-
!parameters.body.some((item) =>
416-
item.name === _name
417-
)
419+
!parameters.body.some((item) => item.name === _name)
418420
) {
419421
parameters.body.push(_body);
420422
}

src/i18n/locales/en.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"timeout": "Request timed out."
1111
},
1212
"def": {
13-
"parserTypeError": "Cannot parse {{name}} type {{type}} as it is not an object type."
13+
"parserTypeError": "{{name}} with type {{type}} is ignored as it is not an object type."
1414
},
1515
"path": {
1616
"notName": "{{url}} [{{method}}] cannot get the method name, so it will be ignored.",
@@ -42,7 +42,8 @@
4242
"option_version": "View version information.",
4343
"option_conjunction": "Conjunction of method name. [default: \"By\"].",
4444
"option_actionIndex": "Method name index. [default: -1].",
45-
"option_shared": "Whether to generate the shared directory. [default: true]."
45+
"option_shared": "Whether to generate the shared directory. [default: true].",
46+
"option_clean": "Whether to clean the output directory before generating. [default: true]."
4647
},
4748
"plugin": {
4849
"name": "Load plugin {{name}}",
@@ -55,11 +56,11 @@
5556
"no_200_response": "Missing information for 200 status code.",
5657
"no_tag": "{{url}} does not specify a tag, skip parsing. Please use the --tag parameter.",
5758
"template": {
58-
"enumRequired": "Missing enum.eta template file",
59-
"actionImportRequired": "Missing actionImport.eta template file",
60-
"definitionHeader": "Missing definitionHeader.eta template file",
61-
"definitionBody": "Missing definitionBody.eta template file",
62-
"definitionFooter": "Missing definitionFooter.eta template file"
59+
"enumRequired": "Missing enum.eta template file.",
60+
"actionImportRequired": "Missing actionImport.eta template file.",
61+
"definitionHeader": "Missing definitionHeader.eta template file.",
62+
"definitionBody": "Missing definitionBody.eta template file.",
63+
"definitionFooter": "Missing definitionFooter.eta template file."
6364
}
6465
},
6566
"plugin_javascript": {

src/i18n/locales/zh-CN.json

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"timeout": "请求超时。"
1111
},
1212
"def": {
13-
"parserTypeError": "{{name}} 类型 {{type}},非 object 类型无法解析"
13+
"parserTypeError": "{{name}} 类型为 {{type}},非 object 类型已忽略"
1414
},
1515
"path": {
1616
"notName": "{{url}} 【{{method}}】 无法获取方法名称,故忽略。",
@@ -42,7 +42,8 @@
4242
"option_version": "显示版本信息。",
4343
"option_conjunction": "方法名的连接词。 [默认: \"By\"]。",
4444
"option_actionIndex": "方法名的索引。 [默认: -1]。",
45-
"option_shared": "是否生成 shared 目录。 [默认: true]。"
45+
"option_shared": "是否生成 shared 目录。 [默认: true]。",
46+
"option_clean": "是否清空输出目录。 [默认: true]。"
4647
},
4748
"plugin": {
4849
"name": "加载插件 {{name}}",
@@ -55,11 +56,11 @@
5556
"no_200_response": "缺少 200 状态码的信息。",
5657
"no_tag": "{{url}} 未指定 tag,跳过解析。请使用 --tag 参数。",
5758
"template": {
58-
"enumRequired": "缺失 enum.eta 模板文件",
59-
"actionImportRequired": "缺失 actionImport.eta 模板文件",
60-
"definitionHeader": "缺失 definitionHeader.eta 模板文件",
61-
"definitionBody": "缺失 definitionBody.eta 模板文件",
62-
"definitionFooter": "缺失 definitionFooter.eta 模板文件"
59+
"enumRequired": "缺失 enum.eta 模板文件",
60+
"actionImportRequired": "缺失 actionImport.eta 模板文件",
61+
"definitionHeader": "缺失 definitionHeader.eta 模板文件",
62+
"definitionBody": "缺失 definitionBody.eta 模板文件",
63+
"definitionFooter": "缺失 definitionFooter.eta 模板文件"
6364
}
6465
},
6566
"plugin_javascript": {

src/npm/pkg.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lonu/stc",
3-
"version": "2.11.0",
3+
"version": "2.12.0",
44
"description": "A tool for converting OpenApi/Swagger/Apifox into code.",
55
"type": "module",
66
"module": "esm/mod.js",

src/plugins/typescript/shared/apiClientBase.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export interface ApiClientConfig {
2424
* 忽略错误发生的 url 或 baseURL,不触发 error 回调函数。示例:/api/test
2525
*/
2626
errorIgnore?: string[]
27-
config?: Pick<ApiClientConfig, 'baseURL' | 'timeout' | 'signal'>
27+
config?: Pick<ApiClientConfig, 'baseURL' | 'timeout' | 'signal'> & { headers?: IDefaultObject}
2828
/**
2929
* 错误回调函数
3030
*/

src/plugins/typescript/shared/axios/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ export const request = <T>(
105105
method: config.method,
106106
data: _data,
107107
params: config.params?.query,
108-
headers: config.params?.header as AxiosHeaders,
108+
headers: {...config.params?.header, ...config.config.headers} as AxiosHeaders,
109109
timeout: config.config?.timeout,
110110
signal: config.config?.signal,
111111
baseURL: config.config?.baseURL,

0 commit comments

Comments
 (0)