Skip to content

Commit

Permalink
feat: sup responseType infer and generic
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr.Mao committed Mar 14, 2023
1 parent 8e605e6 commit a33a948
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 27 deletions.
2 changes: 1 addition & 1 deletion apipgen.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig } from 'apipgen'

const config = defineConfig({
pipeline: 'swag-got-ts',
pipeline: 'swag-axios-js',
input: {
uri: 'https://petstore.swagger.io/v2/swagger.json',
},
Expand Down
45 changes: 34 additions & 11 deletions packages/apipgen-parser-swagger/src/transform/parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,37 @@ export interface ParameterTransformOptions {
description: string[]
responseType: string
syntax: 'typescript' | 'ecmascript'
returnType?: boolean
generic?: string
}
export function transformParameters(parameters: StatementField[], options: ParameterTransformOptions) {
const { configRead, syntax, interfaces, description, responseType, returnType = true } = options
const typeImport = configRead.outputs.find(v => v.type === 'typings')?.import
const isGenerateType = configRead.outputs.map(v => v.type).includes('typings')
const TypeNamespace = syntax === 'ecmascript' ? `import('${typeImport}')` : 'Types'
const spaceResponseType = `${TypeNamespace}.Response<${spliceTypeSpace(responseType)}>`
const { configRead, syntax, interfaces, description, responseType } = options
const importType = configRead.outputs.find(v => v.type === 'typings')?.import
const isGenerate = configRead.outputs.map(v => v.type).includes('typings')
const namespace = syntax === 'ecmascript' ? `import('${importType}')` : 'Types'
const infer = configRead.graphs.response.infer || ''
const generic = parseGenericType(configRead.graphs.response.generic || options.generic, syntax)
const spaceResponseType = parseResponseType({
responseType: spliceTypeSpace(responseType),
generic,
infer,
namespace,
})

for (const parameter of parameters || []) {
if (!parameter.type)
continue
parameter.type = spliceTypeSpace(parameter.type)

if (syntax !== 'ecmascript')
continue
if (isGenerateType)
if (isGenerate)
description.push(`@param {${parameter.type}${parameter.required ? '' : '='}} ${parameter.name}`)
parameter.type = undefined
parameter.required = true
}

if (isGenerateType && syntax === 'ecmascript' && returnType)
description.push(`@return {Promise<${spaceResponseType}>}`)
if (isGenerate && syntax === 'ecmascript')
description.push(`@return {${spaceResponseType}}`)

function splitTypeSpaces(name: string) {
const _name = name
Expand All @@ -45,9 +53,24 @@ export function transformParameters(parameters: StatementField[], options: Param
return splitTypeSpaces(name)
if (name.includes('|') && name.includes('[]'))
return `${splitTypeSpaces(name)}[]`
const someType = interfaces.map(v => v.name).includes(name.replace('[]', ''))
return (isGenerateType && someType) ? `${TypeNamespace}.${name}` : name
const isExists = interfaces.map(v => v.name).includes(name.replace('[]', ''))
return (isGenerate && isExists) ? `${namespace}.${name}` : name
}

return { spaceResponseType }
}

function parseGenericType(generic = '', syntax: 'typescript' | 'ecmascript') {
if (!generic)
generic = syntax === 'ecmascript' ? 'Promise<{__type__}>' : '{__type__}'
return generic
}

function parseResponseType({ responseType = '', namespace = '', generic = '', infer = '' }) {
if (infer)
responseType = `${namespace}.Infer<${responseType}>`

responseType = generic.replace('{__type__}', responseType)

return responseType
}
12 changes: 8 additions & 4 deletions packages/apipgen-pipeline/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ export function readConfig(config: ApiPipeline.Config) {
config.import = config.import || {}
config.output = config.output || {}
config.output.main = config.output.main || 'src/api/index.ts'
config.responseType = config.responseType || 'T'
config.responseType = config.responseType || {}

if (config.output?.type !== false)
config.output.type = config.output.type || config.output.main.replace(/\.ts|\.js/g, '.type.ts')
if (typeof config.responseType === 'string')
config.responseType = { infer: config.responseType }

const userRoot = process.cwd()
const isTypescript = config.output.main.endsWith('.ts')
Expand All @@ -32,8 +35,8 @@ export function readConfig(config: ApiPipeline.Config) {
},
]

const typings: StatementTypeAlias[] = [
{ export: true, name: 'Response<T>', value: config.responseType || 'T' },
const typings: (StatementTypeAlias | boolean)[] = [
!!config.responseType.infer && { export: true, name: 'Infer<T>', value: config.responseType.infer! },
]

if (config.output.type !== false) {
Expand Down Expand Up @@ -62,7 +65,8 @@ export function readConfig(config: ApiPipeline.Config) {
comments: [],
functions: [],
interfaces: [],
typings,
typings: typings.filter(Boolean) as StatementTypeAlias[],
response: config.responseType,
},
}

Expand Down
23 changes: 20 additions & 3 deletions packages/apipgen/typings/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { StatementFunction, StatementImported, StatementInterface, StatementTypeAlias, StatementVariable } from './statement'
import type { StatementFunction, StatementImported, StatementInterface, StatementResponse, StatementTypeAlias, StatementVariable } from './statement'

namespace ApiPipeline {
export interface Output {
Expand Down Expand Up @@ -44,10 +44,25 @@ namespace ApiPipeline {
type?: string
}
/**
* Type conversion of response body
* type conversion of response body
* @template `T extends { data?: infer V } ? V : void`
*/
responseType?: string
responseType?: string | {
/**
* External generic type definition
*
* @template `Promise<AxiosResponse<{__type__}>>`
*
* @default {__type__}
*/
generic?: string
/**
* type conversion of response body
*
* @template `T extends { data?: infer V } ? V : void`
*/
infer?: string
}
/** Mandatory parameters optional */
paramsPartial?: boolean
}
Expand Down Expand Up @@ -88,6 +103,8 @@ namespace ApiPipeline {
* all request interfaces
*/
interfaces: StatementInterface[]

response: StatementResponse
}
export interface ConfigRead<Config = ApiPipeline.Config> {
/**
Expand Down
5 changes: 5 additions & 0 deletions packages/apipgen/typings/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,8 @@ export interface StatementTypeAlias {
name: string
value: string
}

export interface StatementResponse {
generic?: string
infer?: string
}
7 changes: 3 additions & 4 deletions packages/swag-axios-js/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,17 +68,16 @@ export function transformPaths(paths: Paths, { configRead, functions, interfaces
if (configRead.config.baseURL)
options.unshift('baseURL')

const { spaceResponseType } = transformParameters(parameters, {
transformParameters(parameters, {
syntax: 'ecmascript',
returnType: false,
configRead,
description,
interfaces,
responseType,
generic: 'import(\'axios\').AxiosResponse<{__type__}>',
})
url = transformUrlSyntax(url)

description.push(`@return {import('axios').AxiosResponse<${spaceResponseType}>}`)
url = transformUrlSyntax(url)

functions.push({
export: true,
Expand Down
6 changes: 2 additions & 4 deletions packages/swag-ky-js/src/parser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,18 @@ export function transformPaths(paths: Paths, { configRead, functions, interfaces
if (configRead.config.baseURL)
options.unshift(['prefixUrl', 'baseURL'])

const { spaceResponseType } = transformParameters(parameters, {
transformParameters(parameters, {
syntax: 'ecmascript',
returnType: false,
configRead,
description,
interfaces,
responseType,
generic: 'import(\'ky\').KyResponse<{__type__}>',
})
transformBodyStringify('body', { options, parameters })
transformQueryParams('query', { optionKey: 'searchParams', options })
url = transformUrlSyntax(url)

description.push(`@return {import('ky').KyResponse<${spaceResponseType}>}`)

functions.push({
export: true,
async: true,
Expand Down

0 comments on commit a33a948

Please sign in to comment.