Skip to content

Commit 35863bc

Browse files
committed
feat: 输出文件放到src目录
BREAKING CHANGE: client服务改为从相对路径引用
1 parent 9a6924d commit 35863bc

File tree

13 files changed

+270
-524
lines changed

13 files changed

+270
-524
lines changed

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,4 @@ coverage/
1313
*.d.mts
1414
*.js.map
1515
/adapters
16-
openapi/openapi.yaml
17-
openapi/openapi.json
16+
openapi/

README.md

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export default defineConfig({
3333

3434
## 2. 执行指令
3535

36-
指令的作用是把openapi文档转换为前端服务,代码会自动合并到库文件中
36+
指令的作用是把openapi文档转换为前端服务,生成的文件会自动放到 src/openapi 目录
3737

3838
```bash
3939
npx openapi
@@ -44,8 +44,8 @@ npx openapi
4444
使用合适的请求适配器创建好服务后,就可以导出给各个模块使用了
4545

4646
```typescript
47-
// ./src/services/http.ts
48-
import { OpenapiClient } from 'foca-openapi';
47+
// ./src/openapi/index.ts
48+
import { OpenapiClient } from './openapi/openapi';
4949
import { fetchAdapter } from 'foca-openapi/adapters/fetch';
5050

5151
const adapter = fetchAdapter({ baseURL: 'http://api.com' });
@@ -69,6 +69,7 @@ export const client = new OpenapiClient(adapter);
6969
如果一个项目需要融合多个openapi文档,则可以用数组的形式配置
7070

7171
```typescript
72+
// openapi.config.ts
7273
import { defineConfig } from 'foca-openapi';
7374

7475
export default defineConfig([
@@ -88,7 +89,9 @@ export default defineConfig([
8889
执行指令后就会生成两个类
8990

9091
```typescript
91-
import { OpenapiClientFoo, OpenapiClientBar } from 'foca-openapi';
92+
// ./src/openapi/index.ts
93+
import { OpenapiClientFoo } from './foo';
94+
import { OpenapiClientBar } from './bar';
9295

9396
export const fooClient = new OpenapiClientFoo(adapter1);
9497
export const barClient = new OpenapiClientBar(adapter2);
@@ -144,6 +147,15 @@ npx openapi --config my-custom.config.ts
144147

145148
openapi本地或者远程文件,支持格式:`yaml | json`
146149

150+
### outputFile
151+
152+
类型 `string`<br>
153+
154+
输出文件路径
155+
156+
- 如果没有配置项目名,默认值:`./src/openapi/openapi.ts`
157+
- 如果配置了项目名,默认值:`./src/openapi/${projectName}.ts`
158+
147159
### includeUriPrefix
148160

149161
类型:`string | string[] | RegExp | RegExp[]`

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"scripts": {
66
"test": "vitest",
77
"generate": "aomex openapi-yaml && aomex openapi-json",
8-
"build": "tsup && sh scripts/build-success.sh",
8+
"build": "tsup",
99
"prepare": "husky",
1010
"prepublishOnly": "pnpm build",
1111
"deploy:main": "sh scripts/develop-deploy-main.sh",

scripts/build-success.sh

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/bin.ts

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
#!/usr/bin/env node
22

3-
import path from 'node:path';
4-
import { fileURLToPath } from 'node:url';
53
import timers from 'node:timers/promises';
64
import { Listr } from 'listr2';
75
import minimist from 'minimist';
86
import colors from 'yoctocolors';
97
import { OpenAPIV3 } from 'openapi-types';
108
import type { OpenapiClientConfig } from './define-config';
119
import { pathToOpenapi } from './lib/path-to-openapi';
12-
import { rebuildDist } from './lib/rebuild-dist';
10+
import { saveToFile } from './lib/save-to-file';
1311
import { generateTemplate } from './lib/generate-template';
1412
import { filterTag } from './lib/filter-tag';
1513
import { filterUrl } from './lib/filter-url';
@@ -29,7 +27,7 @@ const toArray = (value: any) => (Array.isArray(value) ? value : [value]);
2927
const spinner = (silent ? new SilentSpinner([]) : new Listr([])) as Listr<{
3028
configs: OpenapiClientConfig[];
3129
docs: OpenAPIV3.Document[];
32-
projects: Record<string, { dts: string; js: string }>;
30+
projects: Record<string, string>;
3331
}>;
3432

3533
spinner.add({
@@ -105,17 +103,13 @@ spinner.add({
105103
});
106104

107105
spinner.add({
108-
title: '写入npm包',
109-
task: async (ctx, task) => {
110-
task.title += ` ${colors.gray(`import { ${Object.keys(ctx.projects).join(', ')} } from 'foca-openapi'`)}`;
111-
const root = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
112-
const jsContent = Object.values(ctx.projects)
113-
.map(({ js }) => js)
114-
.join('\n');
115-
const dtsContent = Object.values(ctx.projects)
116-
.map(({ dts }) => dts)
117-
.join('\n');
118-
await rebuildDist(root, jsContent, dtsContent, Object.keys(ctx.projects));
106+
title: '写入指定文件',
107+
task: async (ctx) => {
108+
await Promise.all(
109+
ctx.configs.map((config) => {
110+
return saveToFile(config, ctx.projects);
111+
}),
112+
);
119113
},
120114
});
121115

src/define-config.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export interface OpenapiClientConfig {
4747
* 加载完openapi文档后的事件,允许直接对文档进行修改
4848
*/
4949
onDocumentLoaded?: (doc: OpenAPIV3.Document) => OpenAPIV3.Document | void;
50+
/**
51+
* 输出文件路径。
52+
*
53+
* - 如果没有配置项目名,默认值:`./src/openapi/openapi.ts`
54+
* - 如果配置了项目名,默认值:`./src/openapi/${projectName}.ts`
55+
*/
56+
outputFile?: string;
5057
}
5158

5259
export type DefineConfigOptions =

src/lib/generate-template.ts

Lines changed: 45 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const generateTemplate = async (
1010
docs: OpenAPIV3.Document,
1111
config: Pick<OpenapiClientConfig, 'projectName' | 'classMode'>,
1212
) => {
13-
const { projectName, classMode = 'rest' } = config;
13+
const { projectName = '', classMode = 'rest' } = config;
1414
const className = `OpenapiClient${upperFirst(camelCase(projectName))}`;
1515
const metas = documentToMeta(docs);
1616

@@ -21,28 +21,27 @@ export const generateTemplate = async (
2121
? generateUriModelClassWithGroup(className, metas)
2222
: generateUriModelClass(className, metas);
2323

24-
const dts = `
24+
const content = `
25+
import { BaseOpenapiClient } from 'foca-openapi';
26+
2527
${generateNamespaceTpl(className, metas)}
26-
${classTpl.dts}
28+
${classTpl}
2729
${generatePathRelationTpl(className, metas)}
30+
${generateContentTypeTpl(metas)}
2831
`;
2932

30-
const js = `
31-
${classTpl.js}
32-
${generateContentTypeTpl(className, metas)}
33-
`;
34-
3533
return {
36-
[className]: {
37-
dts: await prettier.format(dts, { parser: 'typescript', printWidth: 120 }),
38-
js: await prettier.format(js, { parser: 'typescript', printWidth: 120 }),
39-
},
34+
[projectName]: await prettier.format(content, {
35+
parser: 'typescript',
36+
printWidth: 120,
37+
semi: true,
38+
}),
4039
};
4140
};
4241

4342
export const generateNamespaceTpl = (className: string, metas: Metas) => {
4443
return `
45-
declare namespace ${className} {
44+
export namespace ${className} {
4645
${methods
4746
.flatMap((method) => {
4847
let content = metas[method].flatMap((meta) => {
@@ -51,7 +50,7 @@ declare namespace ${className} {
5150
(<const>['query', 'params', 'body', 'response']).forEach((key) => {
5251
const interfaceName = upperFirst(camelCase(meta.key + '_' + key));
5352
if (meta[key].types.length) {
54-
opts.push(`type ${interfaceName} = ${meta[key].types.join(' | ')}\n`);
53+
opts.push(`export type ${interfaceName} = ${meta[key].types.join(' | ')}\n`);
5554
}
5655
});
5756
@@ -64,9 +63,8 @@ declare namespace ${className} {
6463
};
6564

6665
export const generateMethodModeClass = (className: string, metas: Metas) => {
67-
return {
68-
dts: `
69-
declare class ${className}<T extends object = object> extends BaseOpenapiClient<T> {
66+
return `
67+
export class ${className}<T extends object = object> extends BaseOpenapiClient<T> {
7068
${methods
7169
.map((method) => {
7270
if (!metas[method].length) return '';
@@ -90,62 +88,41 @@ declare class ${className}<T extends object = object> extends BaseOpenapiClient<
9088
9189
return `${method}<K extends keyof ${className}_${method}_paths>(
9290
uri: K, ...rest: ${opts}
93-
): Promise<${className}_${method}_paths[K]['response']>`;
94-
})
95-
.join('\n')}
96-
}`,
97-
js: `
98-
var ${className} = class extends BaseOpenapiClient {
99-
${methods
100-
.map((method) => {
101-
if (!metas[method].length) return '';
102-
return `${method}(uri, opts) {
103-
return this.request(uri, "get", opts);
104-
}`;
91+
): Promise<${className}_${method}_paths[K]['response']> {
92+
return this.request(uri, "get", ...rest);
93+
}
94+
`;
10595
})
10696
.join('\n')}
10797
108-
pickContentTypes(uri, method) {
109-
return contentTypes${className}[method + " " + uri] || [void 0, void 0];
98+
protected override pickContentTypes(uri: string, method: string) {
99+
return contentTypes[method + " " + uri] || [void 0, void 0];
110100
}
111-
};`,
112-
};
101+
}`;
113102
};
114103

115104
export const generateUriModelClass = (className: string, metas: Metas) => {
116-
return {
117-
dts: `
118-
declare class ${className}<T extends object = object> extends BaseOpenapiClient<T> {
105+
return `
106+
export class ${className}<T extends object = object> extends BaseOpenapiClient<T> {
119107
${methods
120108
.flatMap((method) => {
121109
return metas[method].map((meta) => {
122110
const optional =
123111
meta.query.optional && meta.params.optional && meta.body.optional;
124112
125113
return `
126-
${generateComments(meta)}${camelCase(meta.key)}(opts${optional ? '?' : ''}: ${className}_${method}_paths['${meta.uri}']['request'] & BaseOpenapiClient.UserInputOpts<T>): Promise<${className}_${method}_paths['${meta.uri}']['response']>`;
127-
});
128-
})
129-
.join('\n')}
130-
}`,
131-
js: `
132-
var ${className} = class extends BaseOpenapiClient {
133-
${methods
134-
.flatMap((method) => {
135-
return metas[method].map((meta) => {
136-
return `
137-
${generateComments(meta)}${camelCase(meta.key)}(opts) {
114+
${generateComments(meta)}${camelCase(meta.key)}(opts${optional ? '?' : ''}: ${className}_${method}_paths['${meta.uri}']['request'] & BaseOpenapiClient.UserInputOpts<T>): Promise<${className}_${method}_paths['${meta.uri}']['response']> {
138115
return this.request('${meta.uri}', "${method}", opts);
139-
}`;
116+
}
117+
`;
140118
});
141119
})
142120
.join('\n')}
143121
144-
pickContentTypes(uri, method) {
145-
return contentTypes${className}[method + " " + uri] || [void 0, void 0];
146-
}
147-
};`,
148-
};
122+
protected override pickContentTypes(uri: string, method: string) {
123+
return contentTypes[method + " " + uri] || [void 0, void 0];
124+
}
125+
}`;
149126
};
150127

151128
export const generateUriModelClassWithGroup = (className: string, metas: Metas) => {
@@ -155,12 +132,11 @@ export const generateUriModelClassWithGroup = (className: string, metas: Metas)
155132
),
156133
];
157134

158-
return {
159-
dts: `
160-
declare class ${className}<T extends object = object> extends BaseOpenapiClient<T> {
135+
return `
136+
export class ${className}<T extends object = object> extends BaseOpenapiClient<T> {
161137
${namespaces
162138
.map((ns) => {
163-
return `readonly ${snakeCase(ns)}: {
139+
return `readonly ${snakeCase(ns)} = {
164140
${methods
165141
.flatMap((method) => {
166142
return metas[method]
@@ -169,44 +145,26 @@ declare class ${className}<T extends object = object> extends BaseOpenapiClient<
169145
const optional =
170146
meta.query.optional && meta.params.optional && meta.body.optional;
171147
172-
return `${generateComments(meta)}${camelCase(meta.key)}(opts${optional ? '?' : ''}: ${className}_${method}_paths['${meta.uri}']['request'] & BaseOpenapiClient.UserInputOpts<T>): Promise<${className}_${method}_paths['${meta.uri}']['response']>`;
173-
});
174-
})
175-
.join('\n')}
176-
}`;
177-
})
178-
.join('\n')}
179-
}`,
180-
js: `
181-
var ${className} = class extends BaseOpenapiClient {
182-
${namespaces
183-
.map((ns) => {
184-
return `${snakeCase(ns)} = {
185-
${methods
186-
.flatMap((method) => {
187-
return metas[method]
188-
.filter((meta) => meta.tags.includes(ns))
189-
.map((meta) => {
190-
return `${generateComments(meta)}${camelCase(meta.key)}: (opts) => {
148+
return `${generateComments(meta)}${camelCase(meta.key)}(opts${optional ? '?' : ''}: ${className}_${method}_paths['${meta.uri}']['request'] & BaseOpenapiClient.UserInputOpts<T>): Promise<${className}_${method}_paths['${meta.uri}']['response']> {
191149
return this.request('${meta.uri}', '${method}', opts);
192-
},`;
150+
}`;
193151
});
194152
})
195-
.join('\n')}
196-
}`;
153+
.join(',\n')}
154+
}`;
197155
})
198156
.join('\n')}
199157
200-
pickContentTypes(uri, method) {
201-
return contentTypes${className}[method + ' ' + uri] || [void 0, void 0];
202-
}
203-
};`,
204-
};
158+
protected override pickContentTypes(uri: string, method: string) {
159+
return contentTypes[method + " " + uri] || [void 0, void 0];
160+
}
161+
}`;
205162
};
206163

207-
export const generateContentTypeTpl = (className: string, metas: Metas) => {
164+
export const generateContentTypeTpl = (metas: Metas) => {
208165
return `
209-
const contentTypes${className} = {
166+
const contentTypes: Record<string, [BaseOpenapiClient.UserInputOpts['requestBodyType'],
167+
BaseOpenapiClient.UserInputOpts['responseType']]> = {
210168
${methods
211169
.map((method) => {
212170
if (!metas[method].length) return '';

0 commit comments

Comments
 (0)