Skip to content

Commit

Permalink
feat: 重构 CLI
Browse files Browse the repository at this point in the history
  • Loading branch information
fjc0k committed Aug 20, 2019
1 parent 02d6b71 commit 70fba8d
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 103 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
},
"dependencies": {
"change-case": "^3.0.2",
"commander": "^2.19.0",
"consola": "^2.3.0",
"fs-extra": "^7.0.1",
"json-schema-generator": "^2.0.6",
Expand Down
11 changes: 8 additions & 3 deletions src/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ export class Generator {
/** { 项目标识: 分类列表 } */
private projectIdToCategoryList: Record<string, CategoryList | undefined> = Object.create(null)

constructor(config: Config) {
constructor(
config: Config,
private options: { cwd: string } = { cwd: process.cwd() },
) {
// config 可能是对象或数组,统一为数组
this.config = castArray(config)
}
Expand Down Expand Up @@ -65,7 +68,7 @@ export class Generator {
syntheticalConfig.prodUrl = projectInfo.getProdUrl(syntheticalConfig.prodEnvName!)
const interfaceList = await this.fetchInterfaceList(syntheticalConfig)
const outputFilePath = path.resolve(
process.cwd(),
this.options.cwd,
syntheticalConfig.outputFilePath!,
)
const categoryUID = `_${serverIndex}_${projectIndex}_${categoryIndex}_${categoryIndex2}`
Expand Down Expand Up @@ -100,7 +103,7 @@ export class Generator {
requestFilePath: (
syntheticalConfig.requestFunctionFilePath
? path.resolve(
process.cwd(),
this.options.cwd,
syntheticalConfig.requestFunctionFilePath,
)
: path.join(
Expand Down Expand Up @@ -259,6 +262,7 @@ export class Generator {
}
break
default:
/* istanbul ignore next */
break
}
break
Expand Down Expand Up @@ -298,6 +302,7 @@ export class Generator {

static async fetchApi<T = any>(url: string, query: Record<string, any>): Promise<T> {
const res = await request.get(url, { qs: query, json: true })
/* istanbul ignore next */
if (res && res.errcode) {
throwError(res.errmsg)
}
Expand Down
153 changes: 82 additions & 71 deletions src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#!/usr/bin/env node
import * as TSNode from 'ts-node'
import cli from 'commander'
import consola from 'consola'
import fs from 'fs-extra'
import ora from 'ora'
Expand All @@ -17,80 +16,92 @@ TSNode.register({
},
})

;(async () => {
export async function run(cwd: string = process.cwd()) {
const pkg = require('../package.json')
const configFile = path.join(process.cwd(), 'ytt.config.ts')
const configFile = path.join(cwd, 'ytt.config.ts')
const cmd = process.argv[2]

cli
.version(pkg.version)
.arguments('[cmd]')
.allowUnknownOption()
.action(async cmd => {
switch (cmd) {
case 'init':
if (await fs.pathExists(configFile)) {
consola.info(`检测到配置文件: ${configFile}`)
const answers = await prompt({
type: 'confirm',
name: 'override',
message: '是否覆盖已有配置文件?',
})
if (!answers.override) return
}
await fs.outputFile(configFile, dedent`
import { Config } from 'yapi-to-typescript'
if (cmd === 'version') {
/* istanbul ignore next */
console.log(`${pkg.name} v${pkg.version}`)
} else if (cmd === 'help') {
/* istanbul ignore next */
console.log(`\n${dedent`
# 用法
初始化配置文件: ytt init
生成代码: ytt
查看版本: ytt version
查看帮助: ytt help
const config: Config = [
{
serverUrl: 'https://foo.bar',
typesOnly: false,
prodEnvName: 'production',
outputFilePath: 'src/api/index.ts',
requestFunctionFilePath: 'src/api/request.ts',
dataKey: 'data',
projects: [
{
token: 'hello',
categories: [
{
id: 50,
getRequestFunctionName(interfaceInfo, changeCase) {
return changeCase.camelCase(
interfaceInfo.parsedPath.name,
)
},
},
],
# GitHub
https://github.com/fjc0k/yapi-to-typescript
`}\n`)
} else if (cmd === 'init') {
if (await fs.pathExists(configFile)) {
consola.info(`检测到配置文件: ${configFile}`)
const answers = await prompt({
type: 'confirm',
name: 'override',
message: '是否覆盖已有配置文件?',
})
if (!answers.override) return
}
await fs.outputFile(configFile, dedent`
import { Config } from 'yapi-to-typescript'
const config: Config = [
{
serverUrl: 'https://foo.bar',
typesOnly: false,
prodEnvName: 'production',
outputFilePath: 'src/api/index.ts',
requestFunctionFilePath: 'src/api/request.ts',
dataKey: 'data',
projects: [
{
token: 'hello',
categories: [
{
id: 50,
getRequestFunctionName(interfaceInfo, changeCase) {
return changeCase.camelCase(
interfaceInfo.parsedPath.name,
)
},
],
},
]
},
],
},
],
},
]
export default config
`)
consola.success('写入配置文件完毕')
} else {
if (!await fs.pathExists(configFile)) {
return consola.error(`找不到配置文件: ${configFile}`)
}
consola.success(`找到配置文件: ${configFile}`)
try {
const config: Config = require(configFile).default
const generator = new Generator(config, { cwd })

export default config
`)
consola.success('写入配置文件完毕')
break
default:
if (!await fs.pathExists(configFile)) {
return consola.error(`找不到配置文件: ${configFile}`)
}
consola.success(`找到配置文件: ${configFile}`)
try {
const config: Config = require(configFile).default
const generator = new Generator(config)
const spinner = ora('正在获取数据并生成代码...').start()
const output = await generator.generate()
spinner.stop()
consola.success('获取数据并生成代码完毕')

const spinner = ora('正在获取数据并生成代码...').start()
const output = await generator.generate()
spinner.stop()
consola.success('获取数据并生成代码完毕')
await generator.write(output)
consola.success('写入文件完毕')
} catch (err) {
/* istanbul ignore next */
return consola.error(err)
}
}
}

await generator.write(output)
consola.success('写入文件完毕')
} catch (err) {
return consola.error(err)
}
break
}
})
.parse(process.argv)
})()
/* istanbul ignore next */
if (require.main === module) {
run()
}
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { PropDefinitions } from './types'
* @param msg 错误信息
*/
export function throwError(...msg: string[]): never {
/* istanbul ignore next */
throw new Error(msg.join(''))
}

Expand Down
53 changes: 25 additions & 28 deletions tests/cli.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import consola from 'consola'
import fs from 'fs-extra'
import path from 'path'
import tempy from 'tempy'
import { run } from '../src/cli'
import { wait } from 'vtils'

function getTempPaths() {
Expand All @@ -21,22 +23,29 @@ function getTempPaths() {
}
}

async function runCli(options: { cmd: string, beforeRun?: () => any }) {
jest.resetModules()
process.argv[2] = options.cmd
options.beforeRun && options.beforeRun()
await import('../src/cli')
async function runCli(cwd: string, cmd: string = '') {
process.argv[2] = cmd
await run(cwd)
await wait(100)
}

describe('cli', () => {
test('正确初始化配置文件 & 生成结果', async () => {
test('没有配置文件生成将会报错', async () => {
const tempPaths = getTempPaths()
const errorHandler = jest.fn()

jest.spyOn(consola, 'error').mockImplementationOnce(errorHandler)

process.chdir(tempPaths.targetDir)
await runCli(tempPaths.targetDir)

expect(errorHandler).toBeCalledTimes(1)
})

test('正确初始化配置文件 & 生成结果', async () => {
const tempPaths = getTempPaths()

// 初始化配置文件
await runCli({ cmd: 'init' })
await wait(1000)
await runCli(tempPaths.targetDir, 'init')
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('配置文件')

// 生成结果
Expand All @@ -47,54 +56,42 @@ describe('cli', () => {
.replace(`dataKey: 'data',`, '')
.replace(`id: 50,`, `id: 58,`),
)
await runCli({ cmd: '' })
await wait(1000)
await runCli(tempPaths.targetDir)
expect(fs.readFileSync(tempPaths.generatedApiFile).toString()).toMatchSnapshot('接口文件')
expect(fs.readFileSync(tempPaths.generatedRequestFile).toString()).toMatchSnapshot('请求文件')
})

test('检查到已有配置,可以选择覆盖', async () => {
const tempPaths = getTempPaths()

process.chdir(tempPaths.targetDir)

// 初始化配置文件
await runCli({ cmd: 'init' })
await wait(1000)
await runCli(tempPaths.targetDir, 'init')
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('配置文件')

// 修改配置文件
fs.writeFileSync(tempPaths.generatedConfigFile, 'hello')
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('修改过的配置文件')

// 覆盖配置文件
await runCli({
cmd: 'init',
beforeRun: () => require('prompts').setAnswer(true),
})
await wait(1000)
require('prompts').setAnswer(true)
await runCli(tempPaths.targetDir, 'init')
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('覆盖后的配置文件')
})

test('检查到已有配置,可以选择不覆盖', async () => {
const tempPaths = getTempPaths()

process.chdir(tempPaths.targetDir)

// 初始化配置文件
await runCli({ cmd: 'init' })
await wait(1000)
await runCli(tempPaths.targetDir, 'init')
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('配置文件')

// 修改配置文件
fs.writeFileSync(tempPaths.generatedConfigFile, 'hello')
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('修改过的配置文件')

// 不覆盖配置文件
await runCli({
cmd: 'init',
beforeRun: () => require('prompts').setAnswer(false),
})
require('prompts').setAnswer(false)
await runCli(tempPaths.targetDir, 'init')
await wait(1000)
expect(fs.readFileSync(tempPaths.generatedConfigFile).toString()).toMatchSnapshot('不覆盖后的配置文件')
})
Expand Down

0 comments on commit 70fba8d

Please sign in to comment.