Skip to content

Commit 0112703

Browse files
committed
feat: add axios, consola, magicast, and nypm dependencies; implement init command for genapi configuration; enhance CLI with improved command handling and error messages
1 parent acf25a2 commit 0112703

11 files changed

Lines changed: 300 additions & 52 deletions

File tree

4 KB
Binary file not shown.

docs/1.guide/1.index.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,23 @@ genapi is a powerful API generator that converts OpenAPI (v2~v3) and other input
44

55
## Installation
66

7-
You can install genapi and presets via npm:
7+
推荐使用 `init` 命令快速生成 genapi 配置文件:
8+
9+
:pm-x{command="genapi init"}
10+
11+
Or 手动安装依赖:
812

913
:pm-install{name="@genapi/core @genapi/presets"}
1014

15+
执行 init 后会进入交互式向导:
16+
17+
1. **选择 Preset**:axios、fetch、ky、got、ofetch、uni
18+
2. **选择输出模式**:TS、JS 或 Schema(仅 fetch、ofetch 支持 Schema)
19+
3. **覆盖确认**:若已存在配置文件,会询问是否覆盖
20+
4. **依赖安装**:可选择立即安装所需依赖,或稍后手动安装
21+
22+
完成后会在项目根目录生成 `genapi.config.ts``genapi.config.js`,随后只需修改 `input` 为你的 API 文档地址即可使用。
23+
1124
## Basic Usage
1225

1326
Create a configuration file `genapi.config.ts` in your project root:

docs/en/1.guide/1.index.md

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,22 @@ genapi is a powerful API generator that converts OpenAPI (v2~v3) and other input
44

55
## Installation
66

7-
You can install genapi via npm:
7+
Use the `init` command to quickly scaffold a genapi configuration file:
88

9-
```bash
10-
npm i @genapi/core @genapi/presets
11-
```
9+
:pm-x{command="genapi init"}
10+
11+
Or install manually:
12+
13+
:pm-install{name="@genapi/core @genapi/presets"}
14+
15+
The init command runs an interactive wizard:
16+
17+
1. **Select Preset**: axios, fetch, ky, got, ofetch, uni
18+
2. **Select Mode**: TS, JS, or Schema (Schema mode available only for fetch and ofetch)
19+
3. **Overwrite Confirmation**: If a config file already exists, you'll be prompted to overwrite
20+
4. **Install Dependencies**: Choose to install required dependencies now, or manually later
21+
22+
After completion, `genapi.config.ts` or `genapi.config.js` will be created in your project root. Update the `input` field with your API documentation URL to get started.
1223

1324
## Basic Usage
1425

packages/core/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,17 @@
3838
"prepublishOnly": "nr build"
3939
},
4040
"dependencies": {
41+
"@clack/prompts": "catalog:runtime",
4142
"@genapi/shared": "workspace:*",
4243
"@hairy/utils": "catalog:runtime",
4344
"cac": "catalog:runtime",
4445
"chalk": "catalog:runtime",
46+
"consola": "catalog:runtime",
4547
"fs-extra": "catalog:runtime",
4648
"jiti": "catalog:runtime",
49+
"knitwork-x": "catalog:runtime",
50+
"magicast": "catalog:runtime",
51+
"nypm": "catalog:runtime",
4752
"ora": "catalog:runtime",
4853
"unconfig": "catalog:runtime"
4954
},

packages/core/src/cli/index.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { ApiPipeline } from '@genapi/shared'
2+
import process from 'node:process'
23
import { merge } from '@hairy/utils'
34
import cac from 'cac'
45
import { loadConfig } from 'unconfig'
56
import { operatePipelineGenerator } from '../generator'
7+
import { initCommand } from './init'
68
import * as parser from './parser'
79

810
const cli = cac('genapi')
@@ -12,13 +14,34 @@ cli
1214
.option('--input <source>', 'The incoming string resolves to a uri or json path.')
1315
.option('--outfile <path>', 'genapi output file options')
1416

17+
cli
18+
.command('init', '初始化 genapi 配置文件')
19+
.action(async () => {
20+
await initCommand()
21+
// initCommand 会自己退出进程
22+
})
23+
1524
parser.readpack(cli)
1625

17-
main()
26+
// 在解析之前检查是否是 init 命令
27+
const rawArgs = process.argv.slice(2)
28+
const isInitCommand = rawArgs[0] === 'init'
29+
30+
if (isInitCommand) {
31+
// 如果是 init 命令,只调用 parse 来触发 action,不执行 main
32+
// initCommand 会在 action 中执行并退出进程
33+
cli.parse()
34+
}
35+
else {
36+
// 只有在不是 init 命令时才执行 main 逻辑
37+
const parsed = cli.parse()
38+
main(parsed)
39+
}
1840

19-
async function main() {
20-
const options = cli.parse().options
21-
if (options.help)
41+
async function main(parsed: ReturnType<typeof cli.parse>) {
42+
const { options } = parsed
43+
44+
if (options.help || options.version)
2245
return
2346

2447
const { config } = await loadConfig<ApiPipeline.DefineConfig>({
@@ -28,6 +51,13 @@ async function main() {
2851
},
2952
})
3053

54+
// 如果配置文件不存在或为空,提示用户
55+
if (!config) {
56+
console.error('错误: 未找到 genapi 配置文件')
57+
console.log('\n提示: 运行 "genapi init" 来创建配置文件\n')
58+
process.exit(1)
59+
}
60+
3161
const servers = parser.servers(config)
3262
await operatePipelineGenerator(servers)
3363
}

packages/core/src/cli/init.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { existsSync, writeFileSync } from 'node:fs'
2+
import { join } from 'node:path'
3+
import process from 'node:process'
4+
import { cancel, confirm, intro, isCancel, outro, select } from '@clack/prompts'
5+
import consola from 'consola'
6+
import { addDependency, addDevDependency, detectPackageManager } from 'nypm'
7+
8+
const logger = consola.withTag('genapi:init')
9+
10+
const CONFIG = {
11+
presets: {
12+
axios: { deps: ['axios'], schema: false },
13+
fetch: { deps: [], schema: true },
14+
ky: { deps: ['ky'], schema: false },
15+
got: { deps: ['got'], schema: false },
16+
ofetch: { deps: ['ofetch'], schema: true },
17+
uni: { deps: ['@uni-helper/uni-network'], schema: false },
18+
} as Record<string, { deps: string[], schema: boolean }>,
19+
}
20+
21+
async function mandate<T>(promise: Promise<any>): Promise<T> {
22+
const res = await promise
23+
if (isCancel(res)) {
24+
cancel('Operation cancelled')
25+
process.exit(0)
26+
}
27+
return res as T
28+
}
29+
30+
export async function initCommand() {
31+
intro('🚀 genapi init')
32+
33+
const preset = await mandate<keyof typeof CONFIG.presets>(select({
34+
message: 'Select preset:',
35+
options: Object.keys(CONFIG.presets).map(v => ({ value: v, label: v })),
36+
}))
37+
38+
const mode = await mandate<('ts' | 'js' | 'schema')>(select({
39+
message: 'Select mode:',
40+
options: [
41+
{ value: 'ts', label: 'TS' },
42+
{ value: 'js', label: 'JS' },
43+
...(CONFIG.presets[preset].schema ? [{ value: 'schema', label: 'Schema' }] : []),
44+
],
45+
}))
46+
47+
const isTS = mode !== 'js'
48+
const fileName = `genapi.config.${isTS ? 'ts' : 'js'}`
49+
const presetValue = `${preset}.${mode}`
50+
const content = isTS
51+
? `import { defineConfig } from '@genapi/core'
52+
import { ${preset} } from '@genapi/presets'
53+
54+
export default defineConfig({ preset: ${presetValue}, input: '...', output: { main: 'src/api/index.ts' } })`
55+
: `const { defineConfig } = require('@genapi/core')
56+
const { ${preset} } = require('@genapi/presets')
57+
58+
module.exports = defineConfig({ preset: ${presetValue}, input: '...', output: { main: 'src/api/index.ts' } })`
59+
60+
if (existsSync(join(process.cwd(), fileName)) && !await mandate(confirm({ message: 'Overwrite?' })))
61+
return
62+
63+
writeFileSync(join(process.cwd(), fileName), content)
64+
65+
// 3. 依赖闭环
66+
const deps = CONFIG.presets[preset].deps
67+
const devDeps = ['@genapi/core', '@genapi/presets', ...(mode === 'schema' ? ['fetchdts'] : [])]
68+
69+
if (await mandate(confirm({ message: 'Install now?' }))) {
70+
addDependency(deps, { cwd: process.cwd() })
71+
addDevDependency(devDeps, { cwd: process.cwd() })
72+
}
73+
else {
74+
const pm = (await detectPackageManager(process.cwd()))?.name || 'npm'
75+
logger.info(`Manual: ${pm} add -D ${deps.join(' ')}`)
76+
}
77+
78+
outro('✨ Success')
79+
}

packages/core/src/cli/parser.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,10 @@ export function options(options: any): Partial<ApiPipeline.Config> {
3636
return config
3737
}
3838

39-
export function servers(config: ApiPipeline.DefineConfig): ApiPipeline.Config[] {
39+
export function servers(config: ApiPipeline.DefineConfig | undefined): ApiPipeline.Config[] {
40+
if (!config)
41+
return []
42+
4043
if (isUndefined((config as ApiPipeline.ConfigServers).servers))
4144
(config as ApiPipeline.ConfigServers).servers = []
4245

playground/genapi.config.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,11 @@
11
import { defineConfig } from '@genapi/core'
2-
// create an API pipeline generator using the pipeline provided by genapi
3-
// each pipeline exposes corresponding methods, which can be reused and reorganized
4-
// import pipeline from '@genapi/pipeline'
52
import { ofetch } from '@genapi/presets'
63

7-
const config = defineConfig({
4+
export default defineConfig({
85
preset: ofetch.schema,
9-
10-
input: 'https://petstore.swagger.io/v2/swagger.json',
6+
input: 'http://example.com/api-docs',
117
output: {
12-
main: 'dist/index.ts',
13-
type: 'dist/index.type.ts',
8+
main: 'src/api/index.ts',
9+
type: 'src/api/index.type.ts',
1410
},
15-
1611
})
17-
18-
export default config

playground/package.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@
33
"type": "module",
44
"private": true,
55
"scripts": {
6-
"build": "genapi"
6+
"build": "genapi",
7+
"init": "genapi init"
78
},
89
"dependencies": {
9-
"@genapi/core": "workspace:*",
1010
"@genapi/pipeline": "workspace:*",
11-
"@genapi/presets": "workspace:*",
1211
"@genapi/transform": "workspace:*",
1312
"wpapi-to-swagger": "catalog:playground"
1413
},
1514
"devDependencies": {
15+
"@genapi/core": "workspace:*",
16+
"@genapi/presets": "workspace:*",
1617
"@uni-helper/uni-network": "catalog:playground",
17-
"fetchdts": "catalog:inlined",
18-
"ofetch": "catalog:inlined"
18+
"axios": "catalog:",
19+
"fetchdts": "catalog:inlined"
1920
}
2021
}

0 commit comments

Comments
 (0)