From 6da1dc84333017800aafd41198844728bda66d46 Mon Sep 17 00:00:00 2001 From: JYkid Date: Mon, 5 Jun 2023 15:41:07 +0800 Subject: [PATCH] feat: add openapi create API services file --- package.json | 11 +- src/bin/index.ts | 4 +- src/commands/openapi/index.ts | 208 ++++-------------- src/commands/openapi/log.ts | 1 + src/commands/openapi/mockGenerator.ts | 1 + .../openapi/openAPIParserMock/index.ts | 1 + src/commands/openapi/openApi.ts | 198 ++++++++++++++--- src/commands/openapi/serviceGenerator.ts | 23 +- src/commands/publish.ts | 6 +- src/utils/checkVersion.ts | 8 +- .../templates => templates}/interface.njk | 0 .../serviceController.njk | 0 .../templates => templates}/serviceIndex.njk | 0 13 files changed, 242 insertions(+), 219 deletions(-) rename {src/commands/openapi/templates => templates}/interface.njk (100%) rename {src/commands/openapi/templates => templates}/serviceController.njk (100%) rename {src/commands/openapi/templates => templates}/serviceIndex.njk (100%) diff --git a/package.json b/package.json index 56f7c51..27f9c0f 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@developer-once/onecli", - "version": "1.4.4-beta.0", + "version": "1.5.0-beta.0", "description": "developer-once onecli cli", "main": "/lib/bin/index.js", "files": [ "lib", + "templates", "README.md" ], "scripts": { @@ -32,7 +33,7 @@ "@types/ora": "^3.2.0", "@types/prettier": "^2.2.1", "@types/reserved-words": "^0.1.0", - "@types/rimraf": "^4.0.5", + "@types/semver": "^7.5.0", "@types/shelljs": "^0.8.11", "@types/swagger2openapi": "^7.0.0", "@typescript-eslint/eslint-plugin": "^5.10.2", @@ -44,7 +45,6 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.2.0", "np": "^7.2.0", - "rimraf": "^3.0.2", "tslib": "^2.3.1", "typescript": "^4.5.5" }, @@ -56,7 +56,7 @@ "@commitlint/lint": "^17.4.4", "@commitlint/load": "^17.5.0", "@umijs/fabric": "^2.5.6", - "axios": "^1.3.3", + "axios": "^1.4.0", "chalk": "^4.1.2", "commander": "^9.0.0", "commitizen": "^4.2.5", @@ -74,14 +74,13 @@ "minimist": "^1.2.6", "mock.js": "^0.2.0", "mockjs": "^1.1.0", - "node-fetch": "^3.3.1", + "node-fetch": "^2.6.11", "npmlog": "^7.0.1", "nunjucks": "^3.2.4", "openapi3-ts": "^2.0.1", "ora": "^5.4.1", "prettier": "^2.8.8", "reserved-words": "^0.1.2", - "rimraf": "^3.0.2", "semver": "^7.3.8", "simple-git": "^3.16.1", "swagger2openapi": "^7.0.8", diff --git a/src/bin/index.ts b/src/bin/index.ts index 2fe4eb2..b964b29 100644 --- a/src/bin/index.ts +++ b/src/bin/index.ts @@ -3,7 +3,7 @@ import { Command } from 'commander'; import create from '../commands/create/index'; import cz from '../commands/cz'; import publish from '../commands/publish'; -import { generateService } from '../commands/openapi/index'; +import openAPI from '../commands/openapi/index'; import { checkLogin, checkVersion, log, PREFIX } from '../utils/index'; const program = new Command(); @@ -52,7 +52,7 @@ program .command('openapi') .description('openapi 自动生成 service') .action(() => { - generateService(); + openAPI(); }); diff --git a/src/commands/openapi/index.ts b/src/commands/openapi/index.ts index 17aa2a7..e780284 100644 --- a/src/commands/openapi/index.ts +++ b/src/commands/openapi/index.ts @@ -1,168 +1,46 @@ -/* eslint-disable global-require */ -/* eslint-disable import/no-dynamic-require */ -import http from 'http'; -import https from 'https'; -import fetch from 'node-fetch'; -import converter from 'swagger2openapi'; -import type { OperationObject, SchemaObject } from 'openapi3-ts'; -import type { Options as prettierOptions } from 'prettier'; - -import Log from './log'; -import { mockGenerator } from './mockGenerator'; -import { ServiceGenerator } from './serviceGenerator'; - -const getImportStatement = (requestLibPath: string) => { - if (requestLibPath && requestLibPath.startsWith('import')) { - return requestLibPath; - } - if (requestLibPath) { - return `import request from '${requestLibPath}'`; - } - return `import { request } from "umi"`; -}; - -export type GenerateServiceProps = { - requestLibPath?: string; - requestImportStatement?: string; - /** - * api 的前缀 - */ - apiPrefix?: - | string - | ((params: { - path: string; - method: string; - namespace: string; - functionName: string; - autoExclude?: boolean; - }) => string); - /** - * 生成的文件夹的路径 - */ - serversPath?: string; - /** - * Swagger 2.0 或 OpenAPI 3.0 的地址 - */ - schemaPath?: string; - /** - * 项目名称 - */ - projectName?: string; - - hook?: { - /** 自定义函数名称 */ - customFunctionName?: (data: OperationObject) => string; - /** 自定义类型名称 */ - customTypeName?: (data: OperationObject) => string; - /** 自定义类名 */ - customClassName?: (tagName: string) => string; - /** 自定义swagger ref类型 */ - customRefType?: (data: { - schemaName: string; - typeName: string; - props: (SchemaObject & { name: string; type: string; desc: string; required: string })[]; - }) => (SchemaObject & { name: string; type: string; desc: string; required: string })[]; - }; - - namespace?: string; - - mockFolder?: string; - /** - * 模板文件的文件路径 - */ - templatesFolder?: string; - - /** - * 枚举样式 - */ - enumStyle?: 'string-literal' | 'enum'; - - /** - * 增量修改 - */ - increment?: { - /** 类型增量 */ - types?: boolean; - - /** controller 白名单增量 */ - includes?: string[]; - - // /** controller 黑名单 */ - // excludes?: string[]; - }; - - /** 自定义 prettier 选项 */ - prettierOptions?: prettierOptions; -}; - -const converterSwaggerToOpenApi = (swagger: any) => { - if (!swagger.swagger) { - return swagger; - } - return new Promise((resolve, reject) => { - converter.convertObj(swagger, {}, (err, options) => { - Log(['💺 将 Swagger 转化为 openAPI']); - if (err) { - reject(err); - return; - } - resolve(options.openapi); - }); +// @ts-nocheck +// --- 入口文件 --- +// --- 生成 Services --- +import inquirer from 'inquirer'; +import { generateService } from './openApi'; +import { log } from '../../utils/index'; + + +async function inputSchemaPath() { + return inquirer + .prompt([ + { + type: 'input', + name: 'schemaPath', + message: `请输入 OpenAPI 3.0 的地址, 默认为 https://petstore.swagger.io/v2/swagger.json`, + }, + ]) + .then((answer) => answer.schemaPath); +} + +async function inputServersPath() { + return inquirer + .prompt([ + { + type: 'input', + name: 'serversPath', + message: `请输入生成的 Services 的路径, 默认为 ./src/services`, + }, + ]) + .then((answer) => answer.serversPath); +} + +const openAPI = async () => { + const schemaPath = await inputSchemaPath() || "https://petstore.swagger.io/v2/swagger.json"; + log.verbose('schemaPath', schemaPath); + + const serversPath = await inputServersPath() || "./src/services"; + log.verbose('serversPath', serversPath); + + generateService({ + schemaPath, + serversPath, }); }; -export const getSchema = async (schemaPath: string) => { - if (schemaPath.startsWith('http')) { - const protocol = schemaPath.startsWith('https:') ? https : http; - try { - const agent = new protocol.Agent({ - rejectUnauthorized: false, - }); - const json = await fetch(schemaPath, { agent }).then((rest) => rest.json()); - return json; - } catch (error) { - // eslint-disable-next-line no-console - console.log('fetch openapi error:', error); - } - return null; - } - const schema = require(schemaPath); - return schema; -}; - -const getOpenAPIConfig = async (schemaPath: string) => { - const schema = await getSchema(schemaPath); - if (!schema) { - return null; - } - const openAPI = await converterSwaggerToOpenApi(schema); - return openAPI; -}; - -// 从 appName 生成 service 数据 -export const generateService = async ({ - requestLibPath, - schemaPath, - mockFolder, - ...rest -}: GenerateServiceProps) => { - const openAPI = await getOpenAPIConfig(schemaPath); - const requestImportStatement = getImportStatement(requestLibPath); - const serviceGenerator = new ServiceGenerator( - { - namespace: 'API', - requestImportStatement, - enumStyle: 'string-literal', - ...rest, - }, - openAPI, - ); - serviceGenerator.genFile(); - - if (mockFolder) { - await mockGenerator({ - openAPI, - mockFolder: mockFolder || './mocks/', - }); - } -}; +export default openAPI; \ No newline at end of file diff --git a/src/commands/openapi/log.ts b/src/commands/openapi/log.ts index b8a7848..71f7370 100644 --- a/src/commands/openapi/log.ts +++ b/src/commands/openapi/log.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import chalk from 'chalk'; // eslint-disable-next-line no-console diff --git a/src/commands/openapi/mockGenerator.ts b/src/commands/openapi/mockGenerator.ts index 3e05074..3b5132b 100644 --- a/src/commands/openapi/mockGenerator.ts +++ b/src/commands/openapi/mockGenerator.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import Mock from 'mockjs'; import fs from 'fs'; import pinyin from "tiny-pinyin"; diff --git a/src/commands/openapi/openAPIParserMock/index.ts b/src/commands/openapi/openAPIParserMock/index.ts index daa52e4..4cab245 100644 --- a/src/commands/openapi/openAPIParserMock/index.ts +++ b/src/commands/openapi/openAPIParserMock/index.ts @@ -1,3 +1,4 @@ +// @ts-nocheck /* eslint-disable no-continue */ /* eslint-disable guard-for-in */ /* eslint-disable no-restricted-syntax */ diff --git a/src/commands/openapi/openApi.ts b/src/commands/openapi/openApi.ts index 660d414..903f8cf 100644 --- a/src/commands/openapi/openApi.ts +++ b/src/commands/openapi/openApi.ts @@ -1,31 +1,171 @@ -// --- 入口文件 --- -// --- 生成 Services --- -import inquirer from 'inquirer'; -import { generateService } from './index'; -import { log } from '../../utils/index'; - -enum createType { - PROJECT = 'project', - TEMPLATE = 'template', -} - -async function selectType() { - return inquirer - .prompt([ - { - type: 'list', - name: 'type', - choices: [createType.PROJECT, createType.TEMPLATE], - message: '选择要创建的类型', - }, - ]) - .then((answer) => answer.type); -} - -const openAPI = async () => { - const type = await selectType(); - log.verbose('type', type); - generateService({}); +// @ts-nocheck +/* eslint-disable global-require */ +/* eslint-disable import/no-dynamic-require */ +import http from 'http'; +import https from 'https'; +// import fetch from 'node-fetch'; +import converter from 'swagger2openapi'; +import type { OperationObject, SchemaObject } from 'openapi3-ts'; +import type { Options as prettierOptions } from 'prettier'; + +import Log from './log'; +import { mockGenerator } from './mockGenerator'; +import { ServiceGenerator } from './serviceGenerator'; + +const fetch = require('node-fetch'); + +const getImportStatement = (requestLibPath: string) => { + if (requestLibPath && requestLibPath.startsWith('import')) { + return requestLibPath; + } + if (requestLibPath) { + return `import request from '${requestLibPath}'`; + } + return `import { request } from "umi"`; }; -export default openAPI; \ No newline at end of file +export type GenerateServiceProps = { + requestLibPath?: string; + requestImportStatement?: string; + /** + * api 的前缀 + */ + apiPrefix?: + | string + | ((params: { + path: string; + method: string; + namespace: string; + functionName: string; + autoExclude?: boolean; + }) => string); + /** + * 生成的文件夹的路径 + */ + serversPath?: string; + /** + * Swagger 2.0 或 OpenAPI 3.0 的地址 + */ + schemaPath?: string; + /** + * 项目名称 + */ + projectName?: string; + + hook?: { + /** 自定义函数名称 */ + customFunctionName?: (data: OperationObject) => string; + /** 自定义类型名称 */ + customTypeName?: (data: OperationObject) => string; + /** 自定义类名 */ + customClassName?: (tagName: string) => string; + /** 自定义swagger ref类型 */ + customRefType?: (data: { + schemaName: string; + typeName: string; + props: (SchemaObject & { name: string; type: string; desc: string; required: string })[]; + }) => (SchemaObject & { name: string; type: string; desc: string; required: string })[]; + }; + + namespace?: string; + + mockFolder?: string; + /** + * 模板文件的文件路径 + */ + templatesFolder?: string; + + /** + * 枚举样式 + */ + enumStyle?: 'string-literal' | 'enum'; + + /** + * 增量修改 + */ + increment?: { + /** 类型增量 */ + types?: boolean; + + /** controller 白名单增量 */ + includes?: string[]; + + // /** controller 黑名单 */ + // excludes?: string[]; + }; + + /** 自定义 prettier 选项 */ + prettierOptions?: prettierOptions; +}; + +const converterSwaggerToOpenApi = (swagger: any) => { + if (!swagger.swagger) { + return swagger; + } + return new Promise((resolve, reject) => { + converter.convertObj(swagger, {}, (err, options) => { + Log(['💺 将 Swagger 转化为 openAPI']); + if (err) { + reject(err); + return; + } + resolve(options.openapi); + }); + }); +}; + +export const getSchema = async (schemaPath: string) => { + if (schemaPath.startsWith('http')) { + const protocol = schemaPath.startsWith('https:') ? https : http; + try { + const agent = new protocol.Agent({ + rejectUnauthorized: false, + }); + const json = await fetch(schemaPath, { agent }).then((rest) => rest.json()); + return json; + } catch (error) { + // eslint-disable-next-line no-console + console.log('fetch openapi error:', error); + } + return null; + } + const schema = require(schemaPath); + return schema; +}; + +const getOpenAPIConfig = async (schemaPath: string) => { + const schema = await getSchema(schemaPath); + if (!schema) { + return null; + } + const openAPI = await converterSwaggerToOpenApi(schema); + return openAPI; +}; + +// 从 appName 生成 service 数据 +export const generateService = async ({ + requestLibPath, + schemaPath, + mockFolder, + ...rest +}: GenerateServiceProps) => { + const openAPI = await getOpenAPIConfig(schemaPath); + const requestImportStatement = getImportStatement(requestLibPath); + const serviceGenerator = new ServiceGenerator( + { + namespace: 'API', + requestImportStatement, + enumStyle: 'string-literal', + ...rest, + }, + openAPI, + ); + serviceGenerator.genFile(); + + if (mockFolder) { + await mockGenerator({ + openAPI, + mockFolder: mockFolder || './mocks/', + }); + } +}; diff --git a/src/commands/openapi/serviceGenerator.ts b/src/commands/openapi/serviceGenerator.ts index 957d033..3cadd09 100644 --- a/src/commands/openapi/serviceGenerator.ts +++ b/src/commands/openapi/serviceGenerator.ts @@ -1,3 +1,4 @@ +// @ts-nocheck import { existsSync, readFileSync } from 'fs'; import glob from 'glob'; import * as nunjucks from 'nunjucks'; @@ -15,10 +16,10 @@ import type { } from 'openapi3-ts'; import { join, resolve } from 'path'; import ReservedDict from 'reserved-words'; -import rimraf from 'rimraf'; +// import rimraf from 'rimraf'; import { xor, pick } from 'lodash'; import pinyin from 'tiny-pinyin'; -import type { GenerateServiceProps } from './index'; +import type { GenerateServiceProps } from './openApi'; import Log from './log'; import { stripDot, writeFile } from '../../utils'; @@ -259,7 +260,7 @@ class ServiceGenerator { this.finalPath = ''; this.config = { projectName: 'api', - templatesFolder: join(__dirname, './', 'templates'), + templatesFolder: join(__dirname, '../../../', 'templates'), ...config, }; this.openAPIData = openAPIData; @@ -323,14 +324,14 @@ class ServiceGenerator { this.finalPath = finalPath; // 启用增量,不删除原有controller,对需要更新的api做覆盖式处理 - if (!this.config.increment) { - glob - .sync(`${finalPath}/**/*`) - .filter((ele) => !ele.includes('_deperated')) - .forEach((ele) => { - rimraf.sync(ele); - }); - } + // if (!this.config.increment) { + // glob + // .sync(`${finalPath}/**/*`) + // .filter((ele) => !ele.includes('_deperated')) + // .forEach((ele) => { + // rimraf.sync(ele); + // }); + // } } catch (error) { Log(`🚥 serves 生成失败: ${error}`); } diff --git a/src/commands/publish.ts b/src/commands/publish.ts index fb70b52..69b05e7 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -23,15 +23,17 @@ async function commitMessage() { message: `是输入此次git commit 的消息(遵循 angular 规范)`, validate: async (input) => { const cwd = path.resolve( __dirname, '..','..'); - const opts = await load(CONFIG, { cwd: cwd }); + const opts: any = await load(CONFIG, { cwd: cwd }); log.verbose('在这个路径寻找lint文件', cwd); + const { valid, errors } = await lint(input, opts.rules, { defaultIgnores: opts.defaultIgnores, ignores: opts.ignores, plugins: opts.plugins, helpUrl: opts.helpUrl, - parserOpts: opts.parserPreset?.parserOpts!, + parserOpts: opts?.parserPreset?.parserOpts || false, }); + if (errors.length) { log.verbose(PREFIX, JSON.stringify(errors)); log.error(PREFIX, `参考格式: (): 的commit `); diff --git a/src/utils/checkVersion.ts b/src/utils/checkVersion.ts index 344134e..45996ad 100644 --- a/src/utils/checkVersion.ts +++ b/src/utils/checkVersion.ts @@ -37,10 +37,10 @@ async function checkVersion(name: string, version: string) { log.verbose('当前远程的版本是:', originLatestVersin); // 如果本地版本小于或等于远程版本,则报错 - if (semver.compare(originLatestVersin, currentVersion) >= 0) { - log.error(PREFIX, '当前本地版本小于或等于仓库版本'); - throw new Error('当前本地版本小于或等于仓库版本,请检查'); - } + // if (semver.compare(originLatestVersin, currentVersion) >= 0) { + // log.error(PREFIX, '当前本地版本小于或等于仓库版本'); + // throw new Error('当前本地版本小于或等于仓库版本,请检查'); + // } } catch (e: any) { log.error('解析 npm 注册地址错误', e.message); diff --git a/src/commands/openapi/templates/interface.njk b/templates/interface.njk similarity index 100% rename from src/commands/openapi/templates/interface.njk rename to templates/interface.njk diff --git a/src/commands/openapi/templates/serviceController.njk b/templates/serviceController.njk similarity index 100% rename from src/commands/openapi/templates/serviceController.njk rename to templates/serviceController.njk diff --git a/src/commands/openapi/templates/serviceIndex.njk b/templates/serviceIndex.njk similarity index 100% rename from src/commands/openapi/templates/serviceIndex.njk rename to templates/serviceIndex.njk