use IState as a generic type and remove it from api-codegen
This commit is contained in:
parent
6ab69e4141
commit
d51c345867
8 changed files with 42 additions and 30 deletions
27
README.md
27
README.md
|
@ -72,6 +72,7 @@ import {IServerAPI} from '#api/IServerAPI';
|
||||||
export default {
|
export default {
|
||||||
operationId: async (req, state, ctx) => {
|
operationId: async (req, state, ctx) => {
|
||||||
// ...
|
// ...
|
||||||
|
return [status, body];
|
||||||
},
|
},
|
||||||
// ...
|
// ...
|
||||||
} as IServerAPI;
|
} as IServerAPI;
|
||||||
|
@ -84,14 +85,34 @@ Any parameter will be put in `req.{in}.{name}`, where `{in}` is one of `path`, `
|
||||||
`requestBody` will be put in `req.body`.
|
`requestBody` will be put in `req.body`.
|
||||||
#### state
|
#### state
|
||||||
Alias to `ctx.state`
|
Alias to `ctx.state`
|
||||||
|
|
||||||
|
You can specify the type of `ctx.state` by setting the export default type to `IServerAPI<YourStateType>`.
|
||||||
|
```
|
||||||
|
// example
|
||||||
|
import {IServerAPI} from '#api/IHandler';
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
user: {
|
||||||
|
id: number;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export default {
|
||||||
|
// ...
|
||||||
|
operationId: async (req, state, ctx) => {
|
||||||
|
// state has IState type here
|
||||||
|
state.user.id // number
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
} as IServerAPI<IState> // specify ctx.state type to IState
|
||||||
|
```
|
||||||
#### ctx
|
#### ctx
|
||||||
The `ctx` object from koa router. **Avoid to use it** unless required.
|
The `ctx` object from koa router. **Avoid to use it** unless required.
|
||||||
```
|
```
|
||||||
// Don't do this unless required
|
// Don't do this
|
||||||
ctx.body = responseBody;
|
ctx.body = responseBody;
|
||||||
ctx.status = statusCode;
|
ctx.status = statusCode;
|
||||||
// Do this
|
// Do this
|
||||||
res[statusCode](responseBody);
|
return [statusCode, responseBody];
|
||||||
```
|
```
|
||||||
#### return value
|
#### return value
|
||||||
`[status, body]`
|
`[status, body]`
|
||||||
|
@ -497,6 +518,8 @@ This tool only supports `application/json` type for request and response body. A
|
||||||
Other $ref like requestBody, responseBody are not supported currently.
|
Other $ref like requestBody, responseBody are not supported currently.
|
||||||
|
|
||||||
## Versions
|
## Versions
|
||||||
|
#### 2.0.1
|
||||||
|
- use IState as a generic type and remove it from api-codegen.
|
||||||
#### 2.0.0
|
#### 2.0.0
|
||||||
- simplify generated code
|
- simplify generated code
|
||||||
- merge all APIPromise class
|
- merge all APIPromise class
|
||||||
|
|
|
@ -9,7 +9,6 @@ const badArgv = (x, code=1) => {
|
||||||
'Usage: api-codegen <apiDocPath> [flags]',
|
'Usage: api-codegen <apiDocPath> [flags]',
|
||||||
'Flags:',
|
'Flags:',
|
||||||
' -o --outputDir: outputDir',
|
' -o --outputDir: outputDir',
|
||||||
' -s --stateTSPath: ctx.state type definition file path',
|
|
||||||
].join('\n'));
|
].join('\n'));
|
||||||
process.exit(code);
|
process.exit(code);
|
||||||
};
|
};
|
||||||
|
@ -23,8 +22,6 @@ const argAttrs = ['apiDocPath'];
|
||||||
const flag2attr = {
|
const flag2attr = {
|
||||||
o: 'outputDir',
|
o: 'outputDir',
|
||||||
outputDir: 'outputDir',
|
outputDir: 'outputDir',
|
||||||
s: 'stateTSPath',
|
|
||||||
stateTSPath: 'stateTSPath',
|
|
||||||
};
|
};
|
||||||
const requiredAttrs = [
|
const requiredAttrs = [
|
||||||
...argAttrs,
|
...argAttrs,
|
||||||
|
|
1
dist/Config.d.ts
vendored
1
dist/Config.d.ts
vendored
|
@ -11,7 +11,6 @@ export interface ConfigOptional {
|
||||||
routerName: string;
|
routerName: string;
|
||||||
ServerAPITSPath: string;
|
ServerAPITSPath: string;
|
||||||
utilsTSPath: string;
|
utilsTSPath: string;
|
||||||
stateTSPath: string | null;
|
|
||||||
outputDir: string;
|
outputDir: string;
|
||||||
validateStatus: (status: string) => boolean;
|
validateStatus: (status: string) => boolean;
|
||||||
}
|
}
|
||||||
|
|
1
dist/Config.js
vendored
1
dist/Config.js
vendored
|
@ -12,7 +12,6 @@ exports.configDefault = {
|
||||||
// TS path
|
// TS path
|
||||||
ServerAPITSPath: '#ServerAPI',
|
ServerAPITSPath: '#ServerAPI',
|
||||||
utilsTSPath: '@supmiku39/api-ts-gen/utils',
|
utilsTSPath: '@supmiku39/api-ts-gen/utils',
|
||||||
stateTSPath: null,
|
|
||||||
// other
|
// other
|
||||||
outputDir: 'api/generated',
|
outputDir: 'api/generated',
|
||||||
validateStatus: function (status) { return /^2..$/.test(status); },
|
validateStatus: function (status) { return /^2..$/.test(status); },
|
||||||
|
|
18
dist/codegen.js
vendored
18
dist/codegen.js
vendored
|
@ -6,15 +6,13 @@ var Config_1 = require("./Config");
|
||||||
var OpenAPI_1 = require("./OpenAPI");
|
var OpenAPI_1 = require("./OpenAPI");
|
||||||
var CodePrinter_1 = require("./CodePrinter");
|
var CodePrinter_1 = require("./CodePrinter");
|
||||||
function codegenIHandler(funcs, config, cp) {
|
function codegenIHandler(funcs, config, cp) {
|
||||||
var schemasName = config.schemasName, utilsTSPath = config.utilsTSPath, stateTSPath = config.stateTSPath;
|
var schemasName = config.schemasName, utilsTSPath = config.utilsTSPath;
|
||||||
// import
|
// import
|
||||||
cp.writeln("import * as Schemas from './" + schemasName + "'");
|
cp.writeln("import * as Schemas from './" + schemasName + "'");
|
||||||
cp.writeln('import {FullDate, StrictTypeParser as STP, APIPromise} ' +
|
cp.writeln('import {FullDate, StrictTypeParser as STP, APIPromise} ' +
|
||||||
("from '" + utilsTSPath + "'"));
|
("from '" + utilsTSPath + "'"));
|
||||||
cp.writeln('import {RouterContext as CTX} from \'@koa/router\'');
|
cp.writeln('import {RouterContext as CTX} from \'@koa/router\'');
|
||||||
cp.writeln('import {AxiosResponse} from \'axios\'');
|
cp.writeln('import {AxiosResponse} from \'axios\'');
|
||||||
cp.writeln(stateTSPath ?
|
|
||||||
"import IState from '" + stateTSPath + "'" : 'type IState = any');
|
|
||||||
// api req, res types
|
// api req, res types
|
||||||
cp.writeln("export type TAPI = {", 1);
|
cp.writeln("export type TAPI = {", 1);
|
||||||
for (var _i = 0, _a = Object.entries(funcs); _i < _a.length; _i++) {
|
for (var _i = 0, _a = Object.entries(funcs); _i < _a.length; _i++) {
|
||||||
|
@ -62,26 +60,27 @@ function codegenIHandler(funcs, config, cp) {
|
||||||
// export IServerAPI
|
// export IServerAPI
|
||||||
cp.writeln('');
|
cp.writeln('');
|
||||||
cp.writeln('type ValueOf<T> = T[keyof T];');
|
cp.writeln('type ValueOf<T> = T[keyof T];');
|
||||||
cp.writeln('type Dict<T> = {[_: string]: T};');
|
|
||||||
cp.writeln('type RServerAPI<T> = ValueOf<', 1);
|
cp.writeln('type RServerAPI<T> = ValueOf<', 1);
|
||||||
cp.writeln('{[K in keyof T]: T[K] extends void ? [K, any?] : [K, T[K]]}>;', -1, false);
|
cp.writeln('{[K in keyof T]: T[K] extends void ? [K, any?] : [K, T[K]]}>;', -1, false);
|
||||||
cp.writeln('export type IServerAPI = {[K in keyof TAPI]:', 1);
|
cp.writeln('export type IServerAPI<IState=any> = {[K in keyof TAPI]:', 1);
|
||||||
cp.writeln("(req: TAPI[K]['req'], state: IState, ctx: CTX) =>", 1);
|
cp.writeln("(req: TAPI[K]['req'], state: IState, ctx: CTX) =>", 1);
|
||||||
cp.writeln("Promise<RServerAPI<TAPI[K]['res']>>}", -2, false);
|
cp.writeln("Promise<RServerAPI<TAPI[K]['res']>>}", -2, false);
|
||||||
// return
|
// return
|
||||||
return cp.end();
|
return cp.end();
|
||||||
}
|
}
|
||||||
function codegenRouter(funcs, config, cp) {
|
function codegenRouter(funcs, config, cp) {
|
||||||
var schemasName = config.schemasName, ServerAPITSPath = config.ServerAPITSPath, utilsTSPath = config.utilsTSPath, stateTSPath = config.stateTSPath;
|
var schemasName = config.schemasName, IHandlerName = config.IHandlerName, ServerAPITSPath = config.ServerAPITSPath, utilsTSPath = config.utilsTSPath;
|
||||||
// import
|
// import
|
||||||
cp.writeln("import * as Schemas from './" + schemasName + "'");
|
cp.writeln("import * as Schemas from './" + schemasName + "'");
|
||||||
|
cp.writeln("import {IServerAPI} from './" + IHandlerName + "'");
|
||||||
cp.writeln("import * as Router from '@koa/router'");
|
cp.writeln("import * as Router from '@koa/router'");
|
||||||
cp.writeln("import {FullDate, StrictTypeParser as STP} from '" + utilsTSPath + "'");
|
cp.writeln("import {FullDate, StrictTypeParser as STP} from '" + utilsTSPath + "'");
|
||||||
cp.writeln("import * as bodyParser from 'koa-body'");
|
cp.writeln("import * as bodyParser from 'koa-body'");
|
||||||
cp.writeln(stateTSPath ?
|
// api
|
||||||
"import IState from '" + stateTSPath + "'" : 'type IState = any');
|
cp.writeln("\nimport api from '" + ServerAPITSPath + "'");
|
||||||
cp.writeln("type CTX = Router.RouterContext<IState>;");
|
cp.writeln("type IState = typeof api extends IServerAPI<infer T> ? T : any;");
|
||||||
// router
|
// router
|
||||||
|
cp.writeln("type CTX = Router.RouterContext<IState>;");
|
||||||
cp.writeln("\nconst router = new Router<IState>();");
|
cp.writeln("\nconst router = new Router<IState>();");
|
||||||
// function
|
// function
|
||||||
var gcGetParams = {
|
var gcGetParams = {
|
||||||
|
@ -91,7 +90,6 @@ function codegenRouter(funcs, config, cp) {
|
||||||
cookie: function (attr) { return "ctx.cookies.get('" + attr + "')"; },
|
cookie: function (attr) { return "ctx.cookies.get('" + attr + "')"; },
|
||||||
};
|
};
|
||||||
// route
|
// route
|
||||||
cp.writeln("\nimport api from '" + ServerAPITSPath + "'");
|
|
||||||
for (var _i = 0, _a = Object.entries(funcs); _i < _a.length; _i++) {
|
for (var _i = 0, _a = Object.entries(funcs); _i < _a.length; _i++) {
|
||||||
var _b = _a[_i], funcName = _b[0], func = _b[1];
|
var _b = _a[_i], funcName = _b[0], func = _b[1];
|
||||||
var method = func.method, url = func.url, reqTypes = func.reqTypes;
|
var method = func.method, url = func.url, reqTypes = func.reqTypes;
|
||||||
|
|
|
@ -14,7 +14,6 @@ export interface ConfigOptional {
|
||||||
// TS path
|
// TS path
|
||||||
ServerAPITSPath: string;
|
ServerAPITSPath: string;
|
||||||
utilsTSPath: string;
|
utilsTSPath: string;
|
||||||
stateTSPath: string | null;
|
|
||||||
// other
|
// other
|
||||||
outputDir: string;
|
outputDir: string;
|
||||||
validateStatus: (status: string) => boolean;
|
validateStatus: (status: string) => boolean;
|
||||||
|
@ -31,7 +30,6 @@ export const configDefault: ConfigOptional = {
|
||||||
// TS path
|
// TS path
|
||||||
ServerAPITSPath: '#ServerAPI',
|
ServerAPITSPath: '#ServerAPI',
|
||||||
utilsTSPath: '@supmiku39/api-ts-gen/utils',
|
utilsTSPath: '@supmiku39/api-ts-gen/utils',
|
||||||
stateTSPath: null,
|
|
||||||
// other
|
// other
|
||||||
outputDir: 'api/generated',
|
outputDir: 'api/generated',
|
||||||
validateStatus: (status: string) => /^2..$/.test(status),
|
validateStatus: (status: string) => /^2..$/.test(status),
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {CodePrinter} from './CodePrinter';
|
||||||
|
|
||||||
function codegenIHandler(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
function codegenIHandler(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
||||||
const {
|
const {
|
||||||
schemasName, utilsTSPath, stateTSPath,
|
schemasName, utilsTSPath,
|
||||||
} = config;
|
} = config;
|
||||||
// import
|
// import
|
||||||
cp.writeln(`import * as Schemas from './${schemasName}'`);
|
cp.writeln(`import * as Schemas from './${schemasName}'`);
|
||||||
|
@ -17,8 +17,6 @@ function codegenIHandler(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
||||||
`from '${utilsTSPath}'`);
|
`from '${utilsTSPath}'`);
|
||||||
cp.writeln('import {RouterContext as CTX} from \'@koa/router\'');
|
cp.writeln('import {RouterContext as CTX} from \'@koa/router\'');
|
||||||
cp.writeln('import {AxiosResponse} from \'axios\'');
|
cp.writeln('import {AxiosResponse} from \'axios\'');
|
||||||
cp.writeln(stateTSPath ?
|
|
||||||
`import IState from '${stateTSPath}'` : 'type IState = any');
|
|
||||||
// api req, res types
|
// api req, res types
|
||||||
cp.writeln(`export type TAPI = {`, 1);
|
cp.writeln(`export type TAPI = {`, 1);
|
||||||
for (const [funcName, func] of Object.entries(funcs)) {
|
for (const [funcName, func] of Object.entries(funcs)) {
|
||||||
|
@ -60,11 +58,10 @@ function codegenIHandler(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
||||||
// export IServerAPI
|
// export IServerAPI
|
||||||
cp.writeln('');
|
cp.writeln('');
|
||||||
cp.writeln('type ValueOf<T> = T[keyof T];');
|
cp.writeln('type ValueOf<T> = T[keyof T];');
|
||||||
cp.writeln('type Dict<T> = {[_: string]: T};');
|
|
||||||
cp.writeln('type RServerAPI<T> = ValueOf<', 1);
|
cp.writeln('type RServerAPI<T> = ValueOf<', 1);
|
||||||
cp.writeln('{[K in keyof T]: T[K] extends void ? [K, any?] : [K, T[K]]}>;',
|
cp.writeln('{[K in keyof T]: T[K] extends void ? [K, any?] : [K, T[K]]}>;',
|
||||||
-1, false);
|
-1, false);
|
||||||
cp.writeln('export type IServerAPI = {[K in keyof TAPI]:', 1);
|
cp.writeln('export type IServerAPI<IState=any> = {[K in keyof TAPI]:', 1);
|
||||||
cp.writeln(`(req: TAPI[K]['req'], state: IState, ctx: CTX) =>`, 1);
|
cp.writeln(`(req: TAPI[K]['req'], state: IState, ctx: CTX) =>`, 1);
|
||||||
cp.writeln(`Promise<RServerAPI<TAPI[K]['res']>>}`, -2, false);
|
cp.writeln(`Promise<RServerAPI<TAPI[K]['res']>>}`, -2, false);
|
||||||
// return
|
// return
|
||||||
|
@ -72,18 +69,20 @@ function codegenIHandler(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
||||||
}
|
}
|
||||||
function codegenRouter(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
function codegenRouter(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
||||||
const {
|
const {
|
||||||
schemasName, ServerAPITSPath, utilsTSPath, stateTSPath,
|
schemasName, IHandlerName, ServerAPITSPath, utilsTSPath,
|
||||||
} = config;
|
} = config;
|
||||||
// import
|
// import
|
||||||
cp.writeln(`import * as Schemas from './${schemasName}'`);
|
cp.writeln(`import * as Schemas from './${schemasName}'`);
|
||||||
|
cp.writeln(`import {IServerAPI} from './${IHandlerName}'`);
|
||||||
cp.writeln(`import * as Router from '@koa/router'`);
|
cp.writeln(`import * as Router from '@koa/router'`);
|
||||||
cp.writeln(
|
cp.writeln(
|
||||||
`import {FullDate, StrictTypeParser as STP} from '${utilsTSPath}'`);
|
`import {FullDate, StrictTypeParser as STP} from '${utilsTSPath}'`);
|
||||||
cp.writeln(`import * as bodyParser from 'koa-body'`);
|
cp.writeln(`import * as bodyParser from 'koa-body'`);
|
||||||
cp.writeln(stateTSPath ?
|
// api
|
||||||
`import IState from '${stateTSPath}'` : 'type IState = any');
|
cp.writeln(`\nimport api from '${ServerAPITSPath}'`);
|
||||||
cp.writeln(`type CTX = Router.RouterContext<IState>;`);
|
cp.writeln(`type IState = typeof api extends IServerAPI<infer T> ? T : any;`);
|
||||||
// router
|
// router
|
||||||
|
cp.writeln(`type CTX = Router.RouterContext<IState>;`);
|
||||||
cp.writeln(`\nconst router = new Router<IState>();`);
|
cp.writeln(`\nconst router = new Router<IState>();`);
|
||||||
// function
|
// function
|
||||||
const gcGetParams = {
|
const gcGetParams = {
|
||||||
|
@ -93,7 +92,6 @@ function codegenRouter(funcs: APIFuncs, config: Config, cp: CodePrinter) {
|
||||||
cookie: (attr: string) => `ctx.cookies.get('${attr}')`,
|
cookie: (attr: string) => `ctx.cookies.get('${attr}')`,
|
||||||
};
|
};
|
||||||
// route
|
// route
|
||||||
cp.writeln(`\nimport api from '${ServerAPITSPath}'`);
|
|
||||||
for (const [funcName, func] of Object.entries(funcs)) {
|
for (const [funcName, func] of Object.entries(funcs)) {
|
||||||
const {
|
const {
|
||||||
method, url, reqTypes,
|
method, url, reqTypes,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@supmiku39/api-ts-gen",
|
"name": "@supmiku39/api-ts-gen",
|
||||||
"version": "2.0.0-fix01",
|
"version": "2.0.1",
|
||||||
"description": "OpenAPI code generator for TypeScript",
|
"description": "OpenAPI code generator for TypeScript",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
|
|
Reference in a new issue