Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 21 additions & 2 deletions .github/workflows/deno-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ name: Publish to release

on:
push:
branches: ['master', 'main']
branches: ["master", "main"]
paths:
- deno.json

Expand Down Expand Up @@ -55,18 +55,37 @@ jobs:
deno task version
echo "TAG=$(cat ./release/version)" >> $GITHUB_ENV

# 生成变更日志
- name: Generate Changelog
id: changelog
run: |
# 获取上一个tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
# 如果没有上一个tag,获取所有提交记录
CHANGELOG=$(git log --pretty=format:"* %s" --no-merges)
else
# 获取从上一个tag到现在的提交记录
CHANGELOG=$(git log --pretty=format:"* %s" --no-merges ${PREV_TAG}..HEAD)
fi
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
echo "$CHANGELOG" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV

# 发布到 Release
- name: Publish to Release
id: publish_release
uses: softprops/action-gh-release@v2
env:
TAG: ${{ env.TAG }}
CHANGELOG: ${{ env.CHANGELOG }}
with:
files: ./release/!(version)
name: ${{ env.TAG }}
tag_name: ${{ env.TAG }}
body: |
v ${{ env.TAG }}
## What's Changed
${{ env.CHANGELOG }}
draft: false
prerelease: false

Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ App<IAppOption>({
| actionIndex | | number | -1 | The method name index, the default value is `-1`. |
| shared | | boolean | true | Whether to generate the shared directory. [default: true]. |
| clean | | boolean | true | Whether to clean the output directory before generating. [default: true]. |
| globalHeader | gh | string[] | | Global header key configuration, multiple can be set. When a single API has the same key, it will not appear as a parameter. |
| version | v | boolean | | Output version information. |
| help | h | boolean | | Output help information. |

Expand All @@ -153,8 +154,8 @@ Create a `myPlugin.ts` file:

```ts
// 引用模块
// import { start } from 'https://deno.land/x/stc@2.14.5/mod.ts'
import { start } from 'jsr:@lonu/stc@^2.14.5'
// import { start } from 'https://deno.land/x/stc@2.15.0/mod.ts'
import { start } from 'jsr:@lonu/stc@^2.15.0'

// Defining plugins
const myPlugin: IPlugin = {
Expand Down
4 changes: 2 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@lonu/stc",
"version": "2.14.5",
"version": "2.15.0",
"exports": "./mod.ts",
"tasks": {
"pack": "deno run -A src/pack.ts",
"dev": "deno task pack && deno run -A --watch=src src/main.ts --url='https://petstore3.swagger.io/api/v3/openapi.json' --lang=js",
"serve": "deno run -A --watch=src src/service.ts",
"version": "echo '2.14.5' > release/version",
"version": "echo '2.15.0' > release/version",
"build:npm": "deno run -A src/npm/build.ts",
"build:mac": "deno compile -A --target x86_64-apple-darwin --output release/stc src/main.ts",
"build:mac-m": "deno compile -A --target aarch64-apple-darwin --output release/stc-m src/main.ts",
Expand Down
14 changes: 9 additions & 5 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import { trackEvent } from "./ga.ts";

const drawLogo = () => {
console.log(`
______ _______ _______
__________ _______ ___
/ _____|_______|_______)
( (____ _ _
\____ \ | | | |
( (____ _ _
\\____ \\ | | | |
_____) ) | | | |_____
(______/ |_| \______)
(______/ |_| \\______)
`);
};

Expand Down Expand Up @@ -176,6 +176,7 @@ ${getT("$t(cli.option)")}
--actionIndex ${getT("$t(cli.option_actionIndex)")}
--shared ${getT("$t(cli.option_shared)")}
--clean ${getT("$t(cli.option_clean)")}
--globalHeader, --gh ${getT("$t(cli.option_globalHeader)")}
-v, --version ${getT("$t(cli.option_version)")}

${getT("$t(cli.example)")}
Expand All @@ -201,6 +202,7 @@ export const main = async (): Promise<DefaultConfigOptions> => {
"filter",
"conjunction",
"actionIndex",
"globalHeader",
],
alias: {
h: "help",
Expand All @@ -209,8 +211,9 @@ export const main = async (): Promise<DefaultConfigOptions> => {
v: "version",
f: "filter",
c: "conjunction",
gh: "globalHeader",
},
collect: ["filter"],
collect: ["filter", "globalHeader"],
default: {
outDir: "./stc_out",
lang: "ts",
Expand Down Expand Up @@ -270,5 +273,6 @@ export const main = async (): Promise<DefaultConfigOptions> => {
actionIndex: args.actionIndex,
shared: args.shared,
clean: args.clean,
globalHeader: args.globalHeader,
};
};
49 changes: 28 additions & 21 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,36 +356,42 @@ const getProperties = (
* @param url - 接口地址
* @param method - 请求方式
* @param pathMethod - 请求对象
* @param tagIndex - 从 url 指定标签
* @param options - 配置项
* @returns
*/
const getPathVirtualProperty = (
url: string,
method: string,
pathMethod: ISwaggerResultPath,
tagIndex?: number,
options?: DefaultConfigOptions,
): IPathVirtualProperty => {
// 请求参数 path、query、body、formData、header
const parameters =
(pathMethod.parameters?.sort((_a, _b) =>
Number(_b.required) - Number(_a.required)
) ?? []).reduce((prev: IPathVirtualParameter, current) => {
const _schema = current.schema;
const item: IDefinitionVirtualProperty = {
name: current.name,
type: current.type ?? _schema?.type ?? "",
required: current.required,
description: current.description,
format: current.format ?? _schema?.format,
ref: getRefType(
_schema?.$ref ?? _schema?.items?.$ref ?? "",
),
typeX: current?.items?.type ?? _schema?.items?.type,
default: _schema?.default,
enumOption: _schema?.enum,
};

prev[current.in].push(item);
if (
(current.in === "header" &&
!options?.globalHeader?.includes(current.name.toLowerCase())) ||
current.in !== "header"
) {
const _schema = current.schema;
const item: IDefinitionVirtualProperty = {
name: current.name,
type: current.type ?? _schema?.type ?? "",
required: current.required,
description: current.description,
format: current.format ?? _schema?.format,
ref: getRefType(
_schema?.$ref ?? _schema?.items?.$ref ?? "",
),
typeX: current?.items?.type ?? _schema?.items?.type,
default: _schema?.default,
enumOption: _schema?.enum,
};

prev[current.in].push(item);
}

return prev;
}, { path: [], query: [], body: [], formData: [], header: [] });
Expand Down Expand Up @@ -450,8 +456,8 @@ const getPathVirtualProperty = (

// 标签,用于文件名
let _tag = pathMethod.tags?.[0];
if (tagIndex !== undefined) {
_tag = url.split("/")[tagIndex];
if (options?.tag) {
_tag = url.split("/")[options?.tag];
}

const value: IPathVirtualProperty = {
Expand All @@ -477,6 +483,7 @@ const getPathVirtualProperty = (
/**
* 获取接口地址对象
* @param paths - 接口地址
* @param options - 配置项
* @returns
*/
export const getApiPath = (
Expand Down Expand Up @@ -522,7 +529,7 @@ export const getApiPath = (
url,
method,
currentAction,
options?.tag,
options,
);

name = `${value.tag}@${name}`;
Expand Down
3 changes: 2 additions & 1 deletion src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"option_conjunction": "Conjunction of method name. [default: \"By\"].",
"option_actionIndex": "Method name index. [default: -1].",
"option_shared": "Whether to generate the shared directory. [default: true].",
"option_clean": "Whether to clean the output directory before generating. [default: true]."
"option_clean": "Whether to clean the output directory before generating. [default: true].",
"option_globalHeader": "Global header key configuration, multiple can be set. When a single API has the same key, it will not appear as a parameter."
},
"plugin": {
"name": "Load plugin {{name}}",
Expand Down
5 changes: 3 additions & 2 deletions src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,14 @@
"option_out": "输出目录,默认为 stc 当前执行的目录下 {{out}}。",
"option_client": "http 请求客户端。 当 lang 为 ts/js 时,可选值:axios、wechat、fetch。",
"option_lang": "语言,用于输出文件的后缀名, [默认: \"ts\"]。",
"option_filter": "过滤接口,符合过滤条件的接口会被生成。",
"option_filter": "过滤接口,符合过滤条件的接口会被生成,可设置多个。",
"option_tag": "从接口 url 中指定标签,用于文件名,默认读取 tags 的第一个。",
"option_version": "显示版本信息。",
"option_conjunction": "方法名的连接词。 [默认: \"By\"]。",
"option_actionIndex": "方法名的索引。 [默认: -1]。",
"option_shared": "是否生成 shared 目录。 [默认: true]。",
"option_clean": "是否清空输出目录。 [默认: true]。"
"option_clean": "是否清空输出目录。 [默认: true]。",
"option_globalHeader": "全局 header key 配置,可设置多个。当单个 API 存在相同的 key,将不会作为参数出现。"
},
"plugin": {
"name": "加载插件 {{name}}",
Expand Down
2 changes: 1 addition & 1 deletion src/npm/pkg.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lonu/stc",
"version": "2.14.5",
"version": "2.15.0",
"description": "A tool for converting OpenApi/Swagger/Apifox into code.",
"type": "module",
"module": "esm/mod.js",
Expand Down
23 changes: 12 additions & 11 deletions src/plugins/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,16 +149,14 @@ const parseParams = (parameters: IPathVirtualParameter, action: string) =>
};

_params.forEach((item, index) => {
const _type = item.enumOption?.length
? camelCase(`${_defName}_${item.name}`, true)
: `${
convertType(
item.type,
item.typeX ?? item.ref,
item.additionalRef,
pluginOptions,
)
}`;
let _type = `${
convertType(
item.type,
item.typeX ?? item.ref,
item.additionalRef,
pluginOptions,
)
}`;

// 外部引用
if (item.ref && !prev.imports?.includes(item.ref)) {
Expand All @@ -168,6 +166,8 @@ const parseParams = (parameters: IPathVirtualParameter, action: string) =>
/* #region 内部定义 */
// 定义参数枚举
if (item.enumOption?.length) {
_type = camelCase(`${_defName}_${item.name}`, true);

const _enumData = renderEtaString(
pluginOptions.template!.enum,
{ name: _type, data: item.enumOption, convertValue, isEnum: true },
Expand All @@ -180,6 +180,7 @@ const parseParams = (parameters: IPathVirtualParameter, action: string) =>
if (item.properties?.length) {
const _defs = getDefinition(item.properties, _defName);

_type = _defName;
prev.definitions?.push(_defs.join("\n"));
}

Expand Down Expand Up @@ -322,7 +323,7 @@ const generateApi = (data: IPathVirtualProperty, action: string) => {

const _params = parseParams(data.parameters ?? {}, action);
const _response = parseResponse(data.response, action);

// console.log(_params);
if (!_response.name) {
Logs.warn(getT("$t(plugin.no_200_response)"));
}
Expand Down
4 changes: 4 additions & 0 deletions src/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ export interface DefaultConfigOptions {
* 是否清空输出目录。默认:true
*/
readonly clean?: boolean;
/**
* 全局请求头
*/
readonly globalHeader?: string[];
}

export interface IDefinitionNameMapping {
Expand Down
16 changes: 16 additions & 0 deletions test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,19 @@ Deno.test("additionalProperties", async () => {

assertEquals(0, code);
});

Deno.test("globalHeader(gh)", async () => {
const command = new Deno.Command("deno", {
args: [
"run",
"-A",
"src/main.ts",
"--url=https://petstore3.swagger.io/api/v3/openapi.json",
"--globalHeader=authorization",
"--gh=custom-header",
],
});
const { code } = await command.output();

assertEquals(0, code);
});