From 0f1a6a75b452102738364b32d808b0609d37b3a3 Mon Sep 17 00:00:00 2001 From: luochao <1055120207@qq.com> Date: Mon, 15 Jul 2024 23:44:58 +0800 Subject: [PATCH] feat: support work with npx, CLI --- .changeset/fluffy-cars-watch.md | 5 ++ .eslintrc.cjs | 1 + README-en_US.md | 47 ++++++++++++++++- README.md | 45 +++++++++++++++++ package.json | 4 +- pnpm-lock.yaml | 76 +++++++++++++++++++++++++++- src/{ => bin}/cli.ts | 2 +- src/bin/index.ts | 89 +++++++++++++++++++++++++++++++++ src/generator/file.ts | 2 +- templates/serviceIndex.njk | 1 - tsconfig.json | 3 +- 11 files changed, 267 insertions(+), 8 deletions(-) create mode 100644 .changeset/fluffy-cars-watch.md rename src/{ => bin}/cli.ts (90%) create mode 100644 src/bin/index.ts diff --git a/.changeset/fluffy-cars-watch.md b/.changeset/fluffy-cars-watch.md new file mode 100644 index 0000000..56ee602 --- /dev/null +++ b/.changeset/fluffy-cars-watch.md @@ -0,0 +1,5 @@ +--- +'openapi-ts-request': minor +--- + +feat: support work with npx, CLI diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6fef37c..ab4c60a 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -9,6 +9,7 @@ module.exports = { 'dist', 'node_modules', 'test', + '.eslintrc.cjs', 'commitlint.config.cjs', 'lint-staged.config.cjs', 'prettier.config.cjs', diff --git a/README-en_US.md b/README-en_US.md index 2e7768e..1500a2f 100644 --- a/README-en_US.md +++ b/README-en_US.md @@ -12,6 +12,7 @@ Generate TS interfaces, request client, request mock service, enum, type field l * support Swagger2.0/OpenAPI 3.0,3.1 specification * generate TypeScript interface, reuquest client, request mock service, enum, type field label, JSON Schemas +* support work with npx, CLI, Nodejs * support custom request function, Fetch、Axios、UniApp-request、Node.js、XHR client available * support filter generate result by tags * support JSON specification @@ -100,6 +101,50 @@ generate result: npm run openapi ``` +### NPX + +``` +npx openapi-ts-request openapi -i ./openapi.json -o ./apis +``` + +### CLI + +``` +npm i openapi-ts-request -g +``` + +``` +$ openapi --help + + Usage: openapi [options] + + Options: + -V, --version output the version number + -i, --input OpenAPI specification, can be a path, url (required) + -o, --output Output directory (required) + --requestLibPath custom request lib path, for example: "@/request", "node-fetch" + --allowedTags Generate results from allowed tags + --requestOptionsType Custom request method options parameter type (default: "{ [key: + string]: unknown }") + --requestImportStatement custom request import statement, for example: "const request = + require(`@/request`)" + --apiPrefix Custom the prefix of the api path, for example: "api"(variable), + `"api"`(string) + --isDisplayTypeLabel Generate label matching type field (default: false) (default: false) + --isGenJsonSchemas Generate JSON Schemas (default: false) (default: false) + --mockFolder Mock file path, (default: "./mocks") + --nullable null instead of optional (default: false) (default: false) + --isCamelCase CamelCase naming of controller files and request client (default: true) + (default: true) + -h, --help display help for command +``` + +generate result: + +```bash +openapi -i ./spec.json -o ./apis +``` + ## Parameter | props | required | type | default | remark | @@ -110,7 +155,7 @@ npm run openapi | allowedTags | no | string[] | - | generate results from allowed tags | | requestOptionsType | no | string | '{ [key: string]: unknown }' | custom request method options parameter type | | requestImportStatement | no | string | - | custom request import statement, for example: "const request = require('@/request')" | -| apiPrefix | no | string | - | custom the prefix of the api path,例如:'api'(variable), "'api'"(string) | +| apiPrefix | no | string | - | custom the prefix of the api path, for example: 'api'(variable), "'api'"(string) | | isDisplayTypeLabel | no | boolean | false | generate label matching type field | | isGenJsonSchemas | no | boolean | false | generate JSON Schemas | | mockFolder | no | string | './mocks' | mock file path | diff --git a/README.md b/README.md index 2f9bae3..3a77242 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ * 支持 Swagger2.0/OpenAPI 3.0,3.1 定义 * 生成 TS 类型, 请求客户端, 请求模拟服务, 枚举, 类型字段翻译, JSON Schemas +* 支持通过 npx、CLI、Nodejs 的方式使用 * 支持自定义请求工具函数, 支持 Fetch、Axios、UniApp-Request、Node.js、XHR 客户端 * 支持通过 tags 过滤生成结果 * 支持 JSON 定义文件 @@ -100,6 +101,50 @@ generateService({ npm run openapi ``` +### NPX + +``` +npx openapi-ts-request openapi -i ./openapi.json -o ./apis +``` + +### CLI + +``` +npm i openapi-ts-request -g +``` + +``` +$ openapi --help + + Usage: openapi [options] + + Options: + -V, --version output the version number + -i, --input OpenAPI specification, can be a path, url (required) + -o, --output Output directory (required) + --requestLibPath custom request lib path, for example: "@/request", "node-fetch" + --allowedTags Generate results from allowed tags + --requestOptionsType Custom request method options parameter type (default: "{ [key: + string]: unknown }") + --requestImportStatement custom request import statement, for example: "const request = + require(`@/request`)" + --apiPrefix Custom the prefix of the api path, for example: "api"(variable), + `"api"`(string) + --isDisplayTypeLabel Generate label matching type field (default: false) (default: false) + --isGenJsonSchemas Generate JSON Schemas (default: false) (default: false) + --mockFolder Mock file path, (default: "./mocks") + --nullable null instead of optional (default: false) (default: false) + --isCamelCase CamelCase naming of controller files and request client (default: true) + (default: true) + -h, --help display help for command +``` + +生成结果: + +```bash +openapi --i ./spec.json --o ./apis +``` + ## 参数 | 属性 | 必填 | 类型 | 默认值 | 说明 | diff --git a/package.json b/package.json index 7596dda..f8a750f 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "bin": { - "openapi-ts-request": "dist/cli.js" + "openapi": "dist/bin/index.js", + "openapi-ts-request": "dist/bin/cli.js" }, "files": [ "dist", @@ -35,6 +36,7 @@ "@trivago/prettier-plugin-sort-imports": "^4.3.0", "axios": "^1.7.2", "chalk": "^4.1.2", + "commander": "^12.1.0", "cosmiconfig": "^9.0.0", "glob": "^10.4.2", "lodash": "^4.17.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d799578..efdc6f5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: chalk: specifier: ^4.1.2 version: 4.1.2 + commander: + specifier: ^12.1.0 + version: 12.1.0 cosmiconfig: specifier: ^9.0.0 version: 9.0.0(typescript@5.4.5) @@ -37,7 +40,7 @@ importers: version: 1.1.0 nunjucks: specifier: ^3.2.4 - version: 3.2.4 + version: 3.2.4(chokidar@3.6.0) prettier: specifier: ^3.3.2 version: 3.3.2 @@ -656,6 +659,10 @@ packages: zenObservable: optional: true + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -688,6 +695,10 @@ packages: resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} engines: {node: '>=4'} + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} @@ -754,6 +765,10 @@ packages: chardet@0.7.0: resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1216,6 +1231,11 @@ packages: fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} @@ -1415,6 +1435,10 @@ packages: is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + is-docker@3.0.0: resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1863,6 +1887,10 @@ packages: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} engines: {node: ^16.14.0 || >=18.0.0} + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + normalize-url@8.0.1: resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} engines: {node: '>=14.16'} @@ -2148,6 +2176,10 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + reftools@1.1.9: resolution: {integrity: sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==} @@ -3381,6 +3413,12 @@ snapshots: optionalDependencies: rxjs: 6.6.7 + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + optional: true + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -3411,6 +3449,9 @@ snapshots: dependencies: is-windows: 1.0.2 + binary-extensions@2.3.0: + optional: true + bl@4.1.0: dependencies: buffer: 5.7.1 @@ -3495,6 +3536,19 @@ snapshots: chardet@0.7.0: {} + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + optional: true + ci-info@3.9.0: {} clean-stack@4.2.0: @@ -3981,6 +4035,9 @@ snapshots: fs.realpath@1.0.0: {} + fsevents@2.3.3: + optional: true + get-caller-file@2.0.5: {} get-east-asian-width@1.2.0: {} @@ -4203,6 +4260,11 @@ snapshots: is-arrayish@0.2.1: {} + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + optional: true + is-docker@3.0.0: {} is-extglob@2.1.1: {} @@ -4600,6 +4662,9 @@ snapshots: semver: 7.6.2 validate-npm-package-license: 3.0.4 + normalize-path@3.0.0: + optional: true + normalize-url@8.0.1: {} np@10.0.6(typescript@5.4.5): @@ -4663,11 +4728,13 @@ snapshots: number-is-nan@1.0.1: {} - nunjucks@3.2.4: + nunjucks@3.2.4(chokidar@3.6.0): dependencies: a-sync-waterfall: 1.0.1 asap: 2.0.6 commander: 5.1.0 + optionalDependencies: + chokidar: 3.6.0 oas-kit-common@1.0.8: dependencies: @@ -4927,6 +4994,11 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + optional: true + reftools@1.1.9: {} regenerator-runtime@0.14.1: {} diff --git a/src/cli.ts b/src/bin/cli.ts similarity index 90% rename from src/cli.ts rename to src/bin/cli.ts index 6afe96e..c59be18 100644 --- a/src/cli.ts +++ b/src/bin/cli.ts @@ -2,7 +2,7 @@ import chalk from 'chalk'; import { cosmiconfigSync } from 'cosmiconfig'; -import { GenerateServiceProps, generateService } from './index'; +import { GenerateServiceProps, generateService } from '../index'; const explorerSync = cosmiconfigSync('openapi-ts-request'); const config = explorerSync.search()?.config as diff --git a/src/bin/index.ts b/src/bin/index.ts new file mode 100644 index 0000000..6170be0 --- /dev/null +++ b/src/bin/index.ts @@ -0,0 +1,89 @@ +#!/usr/bin/env node +import { program } from 'commander'; +import { join } from 'path'; + +import * as pkg from '../../package.json'; +import { generateService } from '../index'; + +const params = program + .name('openapi') + .usage('[options]') + .version(pkg.version) + .requiredOption( + '-i, --input ', + 'OpenAPI specification, can be a path, url (required)' + ) + .requiredOption('-o, --output ', 'Output directory (required)') + .option( + '--requestLibPath ', + 'custom request lib path, for example: "@/request", "node-fetch"' + ) + .option('--allowedTags ', 'Generate results from allowed tags') + .option( + '--requestOptionsType ', + 'Custom request method options parameter type (default: "{ [key: string]: unknown }")' + ) + .option( + '--requestImportStatement ', + 'custom request import statement, for example: "const request = require(`@/request`)"' + ) + .option( + '--apiPrefix ', + 'Custom the prefix of the api path, for example: "api"(variable), `"api"`(string)' + ) + .option( + '--isDisplayTypeLabel ', + 'Generate label matching type field (default: false)', + false + ) + .option( + '--isGenJsonSchemas ', + 'Generate JSON Schemas (default: false)', + false + ) + .option('--mockFolder ', 'Mock file path, (default: "./mocks")') + .option( + '--nullable ', + 'null instead of optional (default: false)', + false + ) + .option( + '--isCamelCase ', + 'CamelCase naming of controller files and request client (default: true)', + true + ) + .parse(process.argv) + .opts(); + +function getPath(path: string) { + const cwd = process.cwd(); + return join(cwd, path); +} + +async function run() { + try { + const input = getPath(params.input as string); + const output = getPath(params.output as string); + + await generateService({ + schemaPath: input, + serversPath: output, + requestLibPath: params.requestLibPath as string, + allowedTags: params.allowedTags as string[], + requestOptionsType: params.requestOptionsType as string, + apiPrefix: params.apiPrefix as string, + isDisplayTypeLabel: + JSON.parse(params.isDisplayTypeLabel as string) === true, + isGenJsonSchemas: JSON.parse(params.isGenJsonSchemas as string) === true, + mockFolder: params.mockFolder as string, + nullable: JSON.parse(params.nullable as string) === true, + isCamelCase: JSON.parse(params.isCamelCase as string) === true, + }); + process.exit(0); + } catch (error) { + console.error('this is error: ', error); + process.exit(1); + } +} + +void run(); diff --git a/src/generator/file.ts b/src/generator/file.ts index fc862b4..d62cc2b 100644 --- a/src/generator/file.ts +++ b/src/generator/file.ts @@ -12,7 +12,7 @@ export const prettierFile = (content: string): [string, boolean] => { result = format(content, { ...defaultPrettierOptions, parser: 'typescript', - importOrderSeparation: false, + plugins: [], }); } catch (error) { hasError = true; diff --git a/templates/serviceIndex.njk b/templates/serviceIndex.njk index 270cb6b..6e40079 100644 --- a/templates/serviceIndex.njk +++ b/templates/serviceIndex.njk @@ -4,7 +4,6 @@ import * as {{ namespace }} from './{{ interfaceFileName }}'; {%- if isGenJsonSchemas %} import * as schemas from './{{ schemaFileName }}'; {%- endif %} - {% for api in list -%} import * as {{ api.controllerName }} from './{{ api.fileName }}'; {% endfor -%} diff --git a/tsconfig.json b/tsconfig.json index 73da6b9..ade67ea 100755 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,8 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, - "declaration": true + "declaration": true, + "resolveJsonModule": true }, "include": ["src/**/*"], "exclude": [