Skip to content

Commit

Permalink
Merge pull request #20 from long-woo/dev
Browse files Browse the repository at this point in the history
1.1.5
  • Loading branch information
long-woo committed Jul 26, 2023
2 parents 8fbcf15 + d6d8c83 commit ae45fb9
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 45 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ stc --url=https://petstore3.swagger.io/api/v3/openapi.json --outDir=out
| outDir | string | stc_out | 输出目录 |
| platform | string | axios | 平台,可选值:`axios``wechat` |
| lang | string | ts | 语言,用于输出文件的后缀名 |
| tag | number | | 从接口指定标签,默认读取 tags 的第一个用于文件名 |
| tag | number | | 从接口 url 指定标签,默认读取 tags 的第一个用于文件名 |
| filter | string[] | | 过滤接口,符合过滤条件的接口会被生成。eg: `--filter "/pet/*"`,生成 `/pet` 的接口,同时支持多个 `--filter` |

## 插件开发

Expand All @@ -61,7 +62,7 @@ stc --url=https://petstore3.swagger.io/api/v3/openapi.json --outDir=out

```ts
// 引用模块
import { start } from 'https://deno.land/x/stc@1.1.4/mod.ts'
import { start } from 'https://deno.land/x/stc@1.1.5/mod.ts'

// 定义插件
const myPlugin: IPlugin = {
Expand Down
6 changes: 3 additions & 3 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"version": "1.1.4",
"version": "1.1.5",
"tasks": {
"dev": "deno run -A --watch=src src/main.ts --url 'http://127.0.0.1:4523/export/openapi/7?version=3.1' --tag=2 --outDir=out",
"version": "echo '1.1.4' > release/version",
"dev": "deno run -A --watch=src src/main.ts --url 'https://petstore3.swagger.io/api/v3/openapi.json' --outDir=out",
"version": "echo '1.1.5' > release/version",
"build:npm": "deno run -A src/npm/index.ts",
"build:mac": "deno compile -A src/main.ts --output release/stc --target x86_64-apple-darwin",
"build:mac-m": "deno compile -A src/main.ts --output release/stc-m --target aarch64-apple-darwin",
Expand Down
12 changes: 12 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 52 additions & 15 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Args, parse, type ParseOptions } from "std/flags/mod.ts";
import ProgressBar from "x/progress@v1.3.8/mod.ts";

import Logs from "./console.ts";
import { PluginManager } from "./plugins/index.ts";
Expand All @@ -10,7 +11,7 @@ import { ISwaggerOptions, ISwaggerResult } from "./swagger.ts";
import { createAppFile, createFile, emptyDirectory, readFile } from "./util.ts";
import denoJson from "/deno.json" assert { type: "json" };

const checkUpdate = async () => {
const checkUpdate = async (): Promise<string> => {
Logs.info("检查更新...");
const version = Number(denoJson.version?.replace(/\./g, "") ?? 0);

Expand All @@ -27,6 +28,7 @@ const checkUpdate = async () => {
Logs.info("发现新版本,正在更新中...");
const dir = Deno.cwd();
const systemInfo = Deno.build;

const appNameMap: Record<string, string> = {
"x86_64-apple-darwin": "stc",
"aarch64-apple-darwin": "stc-m",
Expand All @@ -39,16 +41,49 @@ const checkUpdate = async () => {
}`;
const downloadApp = await fetch(downloadUrl);

if (downloadApp.ok) {
const content = await downloadApp.arrayBuffer();
const reader = downloadApp.body?.getReader();
// 文件内容长度
const contentLength = Number(downloadApp.headers.get("content-length"));
const size = Number((contentLength / 1024 / 1024).toFixed(1));
// 接收的文件字节长度
let receivedLength = 0;
// 接收到的字节数据
const chunks: Uint8Array[] = [];

if (reader) {
const progressBar = new ProgressBar({
total: size,
display: ":completed/:total M :bar :percent",
});

while (true) {
const { done, value } = await reader.read();

if (done) {
break;
}

chunks.push(value);
receivedLength += value.length;

progressBar.render(Number((receivedLength / 1024 / 1024).toFixed(1)));
}

const chunkContent = new Uint8Array(receivedLength);
let offset = 0;

for (const chunk of chunks) {
chunkContent.set(chunk, offset);
offset += chunk.length;
}

await createAppFile(
`${dir}/stc`,
content,
chunkContent.buffer,
);

Logs.info("更新完成,请重新运行");
return;
Logs.info(`更新完成,版本:v${latestVersion}`);
return latestVersion;
}

Logs.error(downloadApp.statusText);
Expand All @@ -71,11 +106,13 @@ const checkUpdate = async () => {
// }

// Logs.error(new TextDecoder().decode(stderr));
return;
return latestVersion;
}

Logs.info("已经是最新版本");
}

return denoJson.version;
};

/**
Expand Down Expand Up @@ -193,9 +230,8 @@ const printHelp = () => {
-o, --outDir 输出目录,默认为 Deno 当前执行的目录下 stc_out
-p, --platform 平台,可选值:axios、wechat, [default: "axios"]
-l, --lang 语言,用于输出文件的后缀名, [default: "ts"]
--include 包含解析接口
--exclude 排除解析接口
--tag 从接口指定标签,默认读取 tags 的第一个用于文件名
-f, --filter 过滤接口,符合过滤条件的接口会被生成
--tag 从接口 url 中指定标签,默认读取 tags 的第一个用于文件名
-v, --version 显示版本信息
示例:
Expand All @@ -218,15 +254,17 @@ export const main = async (): Promise<ISwaggerOptions> => {
"platform",
"lang",
"tag",
"filter",
],
alias: {
h: "help",
o: "outDir",
p: "platform",
l: "lang",
v: "version",
f: "filter",
},
collect: ["include", "exclude"],
collect: ["filter"],
default: {
lang: "ts",
platform: "axios",
Expand All @@ -242,7 +280,7 @@ export const main = async (): Promise<ISwaggerOptions> => {
const args: Args = parse(Deno.args, argsConfig);

// 检查更新
await checkUpdate();
const _version = await checkUpdate();

// 帮助
if (args.help) {
Expand All @@ -252,7 +290,7 @@ export const main = async (): Promise<ISwaggerOptions> => {

// 版本
if (args.version) {
console.log(`stc v${denoJson.version}`);
console.log(`stc v${_version}`);
Deno.exit(0);
}

Expand Down Expand Up @@ -282,7 +320,6 @@ export const main = async (): Promise<ISwaggerOptions> => {
platform,
lang,
tag: args.tag,
include: args.include,
exclude: args.exclude,
filter: args.filter,
};
};
2 changes: 1 addition & 1 deletion src/npm/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@loongwoo/stc",
"version": "1.1.4",
"version": "1.1.5",
"description": "Swagger 文档转换为接口文件",
"main": "script/cli.js",
"module": "esm/cli.js",
Expand Down
20 changes: 16 additions & 4 deletions src/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,18 @@ const getPathVirtualProperty = (
return value;
};

const parserFilter = (filters: string[]) => {
if (!filters.length) return undefined;

const regStr = filters.reduce((prev, current) => {
const _str = current.replace(/\//g, "\\/").replace(/\*/g, ".*");

return `${prev ? `${prev}|` : ""}(${_str})`;
}, "");

return new RegExp(regStr);
};

/**
* 获取接口地址对象
* @param paths - 接口地址
Expand All @@ -202,10 +214,10 @@ export const getApiPath = (
const pathMap = new Map<string, IPathVirtualProperty>();

Object.keys(paths).forEach((url) => {
// 匹配 include 的规则,再匹配 exclude 的规则,不满足条件直接返回
// if (!["/api/form/getMyFormTemplateList"].includes(url)) {
// return;
// }
// 过滤接口,符合过滤条件的接口会被生成
if (!parserFilter(options?.filter ?? [])?.test(url)) {
return;
}

// 请求方式
const methods = paths[url];
Expand Down
42 changes: 33 additions & 9 deletions src/plugins/typescript/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,17 @@ interface IApiInternalDefinition {
childProps: Array<string>;
}

interface IApiParamsSort {
/**
* 必填参数
*/
required: string[];
/**
* 可选参数
*/
optional: string[];
}

/**
* 接口注释
* @param summary - 主注释
Expand Down Expand Up @@ -181,7 +192,7 @@ const parserParams = (parameters: IPathVirtualParameter, action: string) =>
if (index === 0) {
// 接口参数注释
prev.commit?.push(
`* @param ${current} - ${_defName}`,
`* @param {${_defName}} ${current} - ${_defName}`,
);
// 接口形参
prev.defMap?.push(`${current}: ${_defName}`);
Expand All @@ -190,12 +201,26 @@ const parserParams = (parameters: IPathVirtualParameter, action: string) =>
(prev.refMap[current as keyof IPathVirtualParameter] = current);
}
} else {
// 接口参数注释
prev.commit?.push(
`* @param ${item.name} - ${item.description || item.name}`,
);
// 接口形参
prev.defMap?.push(_defMap);
const _defMapCommit = `* @param {${_type}} ${item.name} - ${
item.description || item.name
}`;
// 找出第一个可选参数的位置
const _optionalIndex = prev.defMap?.findIndex((_d) =>
_d.includes("?:")
) ?? -1;

if (_optionalIndex > -1) {
// 接口参数注释
prev.commit?.splice(_optionalIndex, 0, _defMapCommit);
// 接口形参
prev.defMap?.splice(_optionalIndex, 0, _defMap);
} else {
// 接口参数注释
prev.commit?.push(_defMapCommit);
// 接口形参
prev.defMap?.push(_defMap);
}

// 接口形参使用
if (prev.refMap) {
prev.refMap[current as keyof IPathVirtualParameter] =
Expand Down Expand Up @@ -301,8 +326,7 @@ const generateApi = (data: IPathVirtualProperty, action: string) => {

const _method = `${_methodCommit}
export const ${action} = (${
_params?.defMap?.join(", ") ??
""
_params.defMap?.join(", ") ?? ""
}) => webClient.request<${_response.def}>('${data.url}', '${methodName}'${_methodParam})`;

return {
Expand Down
8 changes: 2 additions & 6 deletions src/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -352,13 +352,9 @@ export interface ISwaggerOptions {
*/
readonly plugins?: IPlugin[];
/**
* 包含解析接口
* 过滤接口,符合过滤条件的接口会被生成
*/
readonly include?: string[];
/**
* 排除解析接口
*/
readonly exclude?: string[];
readonly filter?: string[];
/**
* 从接口指定标签,默认使用 tags 的第一个用于文件名
*/
Expand Down
11 changes: 6 additions & 5 deletions src/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { copy, emptyDir, ensureFile } from "std/fs/mod.ts";
import { format as dateFormat } from "std/datetime/format.ts";
import denoJson from "/deno.json" assert { type: "json" };

interface ICopyFileOptions {
/**
Expand Down Expand Up @@ -40,7 +41,7 @@ export const createFile = async (filePath: string, content: string) => {
await ensureFile(filePath);
await Deno.writeFile(
filePath,
new TextEncoder().encode(`// 由 stc 生成
new TextEncoder().encode(`// 由 stc v${denoJson.version} 生成
// ${dateFormat(new Date(), "yyyy-MM-dd HH:mm:ss")}
${content}`),
Expand All @@ -54,9 +55,10 @@ ${content}`),
* @param {ArrayBuffer} content - 要写入文件的内容
* @return {Promise<void>} - 在文件成功写入时解析,或在出现错误时拒绝
*/

export const createAppFile = (filePath: string, content: ArrayBuffer) =>
Deno.writeFile(filePath, new Uint8Array(content));
export const createAppFile = (
filePath: string,
content: ArrayBuffer,
) => Deno.writeFile(filePath, new Uint8Array(content));

/**
* 覆盖复制文件
Expand Down Expand Up @@ -137,6 +139,5 @@ export const getObjectKeyByValue = (
* @param {string} name - 要检查的键的名称
* @return {boolean} 如果对象具有指定的键,则返回true,否则返回false
*/

export const hasKey = (obj: Record<string, unknown>, name: string) =>
Object.prototype.hasOwnProperty.call(obj, name);

0 comments on commit ae45fb9

Please sign in to comment.