diff --git a/README.md b/README.md index 365598c..b5f87bd 100644 --- a/README.md +++ b/README.md @@ -45,13 +45,16 @@ Options: -o, --output Output directory (default: "openapi") -c, --client HTTP client to generate [fetch, xhr, node, axios, angular] (default: "fetch") --request Path to custom request file - --useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object - --enums Generate JavaScript objects from enum definitions? ['javascript', 'typescript'] - --base Manually set base in OpenAPI config instead of inferring from server value - --serviceResponse Define shape of returned value from service calls ['body', 'generics', 'response'] + --format Process output folder with formatter? ['biome', 'prettier'] + --lint Process output folder with linter? ['eslint', 'biome'] --operationId Use operation ID to generate operation names? - --lint Process output folder with linter? - --format Process output folder with formatter? + --serviceResponse Define shape of returned value from service calls ['body', 'generics', 'response'] + --base Manually set base in OpenAPI config instead of inferring from server value + --enums Generate JavaScript objects from enum definitions? ['javascript', 'typescript'] + --useDateType Use Date type instead of string for date types for models, this will not convert the data to a Date object + --debug Enable debug mode + --noSchemas Disable generating schemas for request and response objects + --schemaTypes Define the type of schema generation ['form', 'json'] (default: "json") -h, --help display help for command ``` @@ -86,11 +89,7 @@ function App() { return (

Pet List

-
    - {data?.map((pet) => ( -
  • {pet.name}
  • - ))} -
+
    {data?.map((pet) =>
  • {pet.name}
  • )}
); } @@ -129,13 +128,7 @@ import { useDefaultClientFindPetsSuspense } from "../openapi/queries/suspense"; function ChildComponent() { const { data } = useDefaultClientFindPetsSuspense({ tags: [], limit: 10 }); - return ( -
    - {data?.map((pet, index) => ( -
  • {pet.name}
  • - ))} -
- ); + return
    {data?.map((pet, index) =>
  • {pet.name}
  • )}
; } function ParentComponent() { diff --git a/examples/react-app/package.json b/examples/react-app/package.json index ae15417..ce60b5f 100644 --- a/examples/react-app/package.json +++ b/examples/react-app/package.json @@ -9,7 +9,7 @@ "dev:mock": "prism mock ./petstore.yaml --dynamic", "build": "tsc && vite build", "preview": "vite preview", - "generate:api": "node ../../dist/cli.mjs -i ./petstore.yaml -c axios --request ./request.ts", + "generate:api": "rimraf ./openapi && node ../../dist/cli.mjs -i ./petstore.yaml -c axios --request ./request.ts", "test:generated": "tsc -p ./tsconfig.openapi.json --noEmit" }, "dependencies": { diff --git a/package.json b/package.json index de9921f..3ddadc2 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "author": "Daiki Urata (@7nohe)", "license": "MIT", "devDependencies": { - "@hey-api/openapi-ts": "0.36.0", + "@hey-api/openapi-ts": "0.42.1", "@types/node": "^20.10.6", "@vitest/coverage-v8": "^1.5.0", "commander": "^12.0.0", @@ -49,11 +49,11 @@ "vitest": "^1.5.0" }, "peerDependencies": { - "@hey-api/openapi-ts": "0.36.0", - "commander": ">= 11 < 13", - "glob": ">= 10", - "ts-morph": ">= 22 < 23", - "typescript": ">= 4.8.3" + "@hey-api/openapi-ts": "0.42.1", + "commander": "12.x", + "glob": "10.x", + "ts-morph": "22.x", + "typescript": "5.x" }, "engines": { "node": ">=14" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af3bd4a..af41a13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: devDependencies: '@hey-api/openapi-ts': - specifier: 0.36.0 - version: 0.36.0(typescript@5.4.3) + specifier: 0.42.1 + version: 0.42.1(typescript@5.4.5) '@types/node': specifier: ^20.10.6 - version: 20.12.2 + version: 20.12.7 '@vitest/coverage-v8': specifier: ^1.5.0 - version: 1.5.0(vitest@1.5.0(@types/node@20.12.2)) + version: 1.5.0(vitest@1.5.0(@types/node@20.12.7)) commander: specifier: ^12.0.0 version: 12.0.0 @@ -31,16 +31,16 @@ importers: version: 22.0.0 typescript: specifier: ^5.3.3 - version: 5.4.3 + version: 5.4.5 vitest: specifier: ^1.5.0 - version: 1.5.0(@types/node@20.12.2) + version: 1.5.0(@types/node@20.12.7) examples/react-app: dependencies: '@tanstack/react-query': specifier: ^5.18.1 - version: 5.28.9(react@18.2.0) + version: 5.29.2(react@18.2.0) axios: specifier: ^1.6.7 version: 1.6.8 @@ -62,22 +62,22 @@ importers: version: 5.7.0 '@types/react': specifier: ^18.2.52 - version: 18.2.73 + version: 18.2.79 '@types/react-dom': specifier: ^18.2.18 - version: 18.2.23 + version: 18.2.25 '@vitejs/plugin-react': specifier: ^4.2.1 - version: 4.2.1(vite@5.2.7(@types/node@20.12.2)) + version: 4.2.1(vite@5.2.10(@types/node@20.12.7)) npm-run-all: specifier: ^4.1.5 version: 4.1.5 typescript: specifier: ^5.3.3 - version: 5.4.3 + version: 5.4.5 vite: specifier: ^5.0.12 - version: 5.2.7(@types/node@20.12.2) + version: 5.2.10(@types/node@20.12.7) packages: @@ -85,24 +85,24 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@apidevtools/json-schema-ref-parser@11.5.4': - resolution: {integrity: sha512-o2fsypTGU0WxRxbax8zQoHiIB4dyrkwYfcm8TxZ+bx9pCzcWZbQtiMqpgBvWA/nJ2TrGjK5adCLfTH8wUeU/Wg==} + '@apidevtools/json-schema-ref-parser@11.5.5': + resolution: {integrity: sha512-hv/aXDILyroHioVW27etFMV+IX6FyNn41YwbeGIAt5h/7fUTQvHI5w3ols8qYAT8aQt3kzexq5ZwxFDxNHIhdQ==} engines: {node: '>= 16'} '@babel/code-frame@7.24.2': resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.24.1': - resolution: {integrity: sha512-Pc65opHDliVpRHuKfzI+gSA4zcgr65O4cl64fFJIWEEh8JoHIHh0Oez1Eo8Arz8zq/JhgKodQaxEwUPRtZylVA==} + '@babel/compat-data@7.24.4': + resolution: {integrity: sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.24.3': - resolution: {integrity: sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==} + '@babel/core@7.24.4': + resolution: {integrity: sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==} engines: {node: '>=6.9.0'} - '@babel/generator@7.24.1': - resolution: {integrity: sha512-DfCRfZsBcrPEHUfuBMgbJ1Ut01Y/itOs+hY2nFLgqsqXd52/iSiVq5TITtUasIUgm+IIKdY2/1I7auiQOEeC9A==} + '@babel/generator@7.24.4': + resolution: {integrity: sha512-Xd6+v6SnjWVx/nus+y0l1sxMOTOMBkyL4+BIdbALyatQnAe/SRVjANeDPSCYaX+i1iJmuGSKf3Z+E+V/va1Hvw==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.23.6': @@ -155,19 +155,14 @@ packages: resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.24.1': - resolution: {integrity: sha512-BpU09QqEe6ZCHuIHFphEFgvNSrubve1FtyMton26ekZ85gRGi6LrTF7zArARp2YvyFxloeiRmtSCq5sjh1WqIg==} + '@babel/helpers@7.24.4': + resolution: {integrity: sha512-FewdlZbSiwaVGlgT1DPANDuCHaDMiOo+D/IDYRFYjHOuv66xMSJ7fQwwODwRNAPkADIO/z1EoF/l2BCWlWABDw==} engines: {node: '>=6.9.0'} '@babel/highlight@7.24.2': resolution: {integrity: sha512-Yac1ao4flkTxTteCDZLEvdxg2fZfz1v8M4QpaGypq/WPDqg3ijHYbDfs+LG5hvzSoqaSZ9/Z9lKSP3CjZjv+pA==} engines: {node: '>=6.9.0'} - '@babel/parser@7.24.1': - resolution: {integrity: sha512-Zo9c7N3xdOIQrNip7Lc9wvRPzlRtovHVE4lkz8WEDr7uYh/GMQhSiIgFxGIArRHYdJE5kxtZjAf8rT0xhdLCzg==} - engines: {node: '>=6.0.0'} - hasBin: true - '@babel/parser@7.24.4': resolution: {integrity: sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==} engines: {node: '>=6.0.0'} @@ -345,8 +340,8 @@ packages: resolution: {integrity: sha512-8YXBE2ZcU/pImVOHX7MWrSR/X5up7t6rPWZlk34RwZEcdr3ua6X+32pSd6XuOQRN+vbuvYNfA6iey8NbrjuMFQ==} engines: {node: '>=14.0.0', npm: '>=6.0.0'} - '@hey-api/openapi-ts@0.36.0': - resolution: {integrity: sha512-vm7td6EAisXiIfaQSZmR/2T7jUt+7YGjXacQ2qSlVlJCQp+RSAhZ9xxx88fAzjYHRdv4V5BXqGl2Q2lSw2sCGg==} + '@hey-api/openapi-ts@0.42.1': + resolution: {integrity: sha512-hLWNkTwvF0xdop8R6WTXNZRS+Nc4qPxf15Bad0Ft03rR/DGi4UkHtrjDsSo7ZqIZjl82mXNOxuX8j6ZS5VF2Gw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -401,78 +396,83 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.13.2': - resolution: {integrity: sha512-3XFIDKWMFZrMnao1mJhnOT1h2g0169Os848NhhmGweEcfJ4rCi+3yMCOLG4zA61rbJdkcrM/DjVZm9Hg5p5w7g==} + '@rollup/rollup-android-arm-eabi@4.16.1': + resolution: {integrity: sha512-92/y0TqNLRYOTXpm6Z7mnpvKAG9P7qmK7yJeRJSdzElNCUnsgbpAsGqerUboYRIQKzgfq4pWu9xVkgpWLfmNsw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.13.2': - resolution: {integrity: sha512-GdxxXbAuM7Y/YQM9/TwwP+L0omeE/lJAR1J+olu36c3LqqZEBdsIWeQ91KBe6nxwOnb06Xh7JS2U5ooWU5/LgQ==} + '@rollup/rollup-android-arm64@4.16.1': + resolution: {integrity: sha512-ttWB6ZCfRLuDIUiE0yiu5gcqOsYjA5F7kEV1ggHMj20FwLZ8A1FMeahZJFl/pnOmcnD2QL0z4AcDuo27utGU8A==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.13.2': - resolution: {integrity: sha512-mCMlpzlBgOTdaFs83I4XRr8wNPveJiJX1RLfv4hggyIVhfB5mJfN4P8Z6yKh+oE4Luz+qq1P3kVdWrCKcMYrrA==} + '@rollup/rollup-darwin-arm64@4.16.1': + resolution: {integrity: sha512-QLDvPLetbqjHojTGFw9+nuSP3YY/iz2k1cep6crYlr97sS+ZJ0W43b8Z0zC00+lnFZj6JSNxiA4DjboNQMuh1A==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.13.2': - resolution: {integrity: sha512-yUoEvnH0FBef/NbB1u6d3HNGyruAKnN74LrPAfDQL3O32e3k3OSfLrPgSJmgb3PJrBZWfPyt6m4ZhAFa2nZp2A==} + '@rollup/rollup-darwin-x64@4.16.1': + resolution: {integrity: sha512-TAUK/D8khRrRIa1KwRzo8JNKk3tcqaeXWdtsiLgA8zmACWwlWLjPCJ4DULGHQrMkeBjp1Cd3Yuwx04lZgFx5Vg==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.13.2': - resolution: {integrity: sha512-GYbLs5ErswU/Xs7aGXqzc3RrdEjKdmoCrgzhJWyFL0r5fL3qd1NPcDKDowDnmcoSiGJeU68/Vy+OMUluRxPiLQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.16.1': + resolution: {integrity: sha512-KO+WGZjrh6zyFTD1alIFkfdtxf8B4BC+hqd3kBZHscPLvE5FR/6QKsyuCT0JlERxxYBSUKNUQ/UHyX5uwO1x2A==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.16.1': + resolution: {integrity: sha512-NqxbllzIB1WoAo4ThUXVtd21iiM5IHMTTXmXySKBLVcZvkU0HIZmatlP7hLzb5yQubcmdIeWmncd2NdsjocEiw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.13.2': - resolution: {integrity: sha512-L1+D8/wqGnKQIlh4Zre9i4R4b4noxzH5DDciyahX4oOz62CphY7WDWqJoQ66zNR4oScLNOqQJfNSIAe/6TPUmQ==} + '@rollup/rollup-linux-arm64-gnu@4.16.1': + resolution: {integrity: sha512-snma5NvV8y7IECQ5rq0sr0f3UUu+92NVmG/913JXJMcXo84h9ak9TA5UI9Cl2XRM9j3m37QwDBtEYnJzRkSmxA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.13.2': - resolution: {integrity: sha512-tK5eoKFkXdz6vjfkSTCupUzCo40xueTOiOO6PeEIadlNBkadH1wNOH8ILCPIl8by/Gmb5AGAeQOFeLev7iZDOA==} + '@rollup/rollup-linux-arm64-musl@4.16.1': + resolution: {integrity: sha512-KOvqGprlD84ueivhCi2flvcUwDRD20mAsE3vxQNVEI2Di9tnPGAfEu6UcrSPZbM+jG2w1oSr43hrPo0RNg6GGg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.13.2': - resolution: {integrity: sha512-zvXvAUGGEYi6tYhcDmb9wlOckVbuD+7z3mzInCSTACJ4DQrdSLPNUeDIcAQW39M3q6PDquqLWu7pnO39uSMRzQ==} - cpu: [ppc64le] + '@rollup/rollup-linux-powerpc64le-gnu@4.16.1': + resolution: {integrity: sha512-/gsNwtiGLqYwN4vP+EIdUC6Q6LTlpupWqokqIndvZcjn9ig/5P01WyaYCU2wvfL/2Z82jp5kX8c1mDBOvCP3zg==} + cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.13.2': - resolution: {integrity: sha512-C3GSKvMtdudHCN5HdmAMSRYR2kkhgdOfye4w0xzyii7lebVr4riCgmM6lRiSCnJn2w1Xz7ZZzHKuLrjx5620kw==} + '@rollup/rollup-linux-riscv64-gnu@4.16.1': + resolution: {integrity: sha512-uU8zuGkQfGqfD9w6VRJZI4IuG4JIfNxxJgEmLMAmPVHREKGsxFVfgHy5c6CexQF2vOfgjB33OsET3Vdn2lln9A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.13.2': - resolution: {integrity: sha512-l4U0KDFwzD36j7HdfJ5/TveEQ1fUTjFFQP5qIt9gBqBgu1G8/kCaq5Ok05kd5TG9F8Lltf3MoYsUMw3rNlJ0Yg==} + '@rollup/rollup-linux-s390x-gnu@4.16.1': + resolution: {integrity: sha512-lsjLtDgtcGFEuBP6yrXwkRN5/wKlvUZtfbKZZu0yaoNpiBL4epgnO21osAALIspVRnl4qZgyLFd8xjCYYWgwfw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.13.2': - resolution: {integrity: sha512-xXMLUAMzrtsvh3cZ448vbXqlUa7ZL8z0MwHp63K2IIID2+DeP5iWIT6g1SN7hg1VxPzqx0xZdiDM9l4n9LRU1A==} + '@rollup/rollup-linux-x64-gnu@4.16.1': + resolution: {integrity: sha512-N2ZizKhUryqqrMfdCnjhJhZRgv61C6gK+hwVtCIKC8ts8J+go+vqENnGexwg21nHIOvLN5mBM8a7DI2vlyIOPg==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.13.2': - resolution: {integrity: sha512-M/JYAWickafUijWPai4ehrjzVPKRCyDb1SLuO+ZyPfoXgeCEAlgPkNXewFZx0zcnoIe3ay4UjXIMdXQXOZXWqA==} + '@rollup/rollup-linux-x64-musl@4.16.1': + resolution: {integrity: sha512-5ICeMxqg66FrOA2AbnBQ2TJVxfvZsKLxmof0ibvPLaYtbsJqnTUtJOofgWb46Gjd4uZcA4rdsp4JCxegzQPqCg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.13.2': - resolution: {integrity: sha512-2YWwoVg9KRkIKaXSh0mz3NmfurpmYoBBTAXA9qt7VXk0Xy12PoOP40EFuau+ajgALbbhi4uTj3tSG3tVseCjuA==} + '@rollup/rollup-win32-arm64-msvc@4.16.1': + resolution: {integrity: sha512-1vIP6Ce02L+qWD7uZYRiFiuAJo3m9kARatWmFSnss0gZnVj2Id7OPUU9gm49JPGasgcR3xMqiH3fqBJ8t00yVg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.13.2': - resolution: {integrity: sha512-2FSsE9aQ6OWD20E498NYKEQLneShWes0NGMPQwxWOdws35qQXH+FplabOSP5zEe1pVjurSDOGEVCE2agFwSEsw==} + '@rollup/rollup-win32-ia32-msvc@4.16.1': + resolution: {integrity: sha512-Y3M92DcVsT6LoP+wrKpoUWPaazaP1fzbNkp0a0ZSj5Y//+pQVfVe/tQdsYQQy7dwXR30ZfALUIc9PCh9Izir6w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.13.2': - resolution: {integrity: sha512-7h7J2nokcdPePdKykd8wtc8QqqkqxIrUz7MHj6aNr8waBRU//NLDVnNjQnqQO6fqtjrtCdftpbTuOKAyrAQETQ==} + '@rollup/rollup-win32-x64-msvc@4.16.1': + resolution: {integrity: sha512-x0fvpHMuF7fK5r8oZxSi8VYXkrVmRgubXpO/wcf15Lk3xZ4Jvvh5oG+u7Su1776A7XzVKZhD2eRc4t7H50gL3w==} cpu: [x64] os: [win32] @@ -544,11 +544,11 @@ packages: resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} engines: {node: '>=10.8'} - '@tanstack/query-core@5.28.9': - resolution: {integrity: sha512-hNlfCiqZevr3GRVPXS3MhaGW5hjcxvCsIQ4q6ff7EPlvFwYZaS+0d9EIIgofnegDaU2BbCDlyURoYfRl5rmzow==} + '@tanstack/query-core@5.29.0': + resolution: {integrity: sha512-WgPTRs58hm9CMzEr5jpISe8HXa3qKQ8CxewdYZeVnA54JrPY9B1CZiwsCoLpLkf0dGRZq+LcX5OiJb0bEsOFww==} - '@tanstack/react-query@5.28.9': - resolution: {integrity: sha512-vwifBkGXsydsLxFOBMe3+f8kvtDoqDRDwUNjPHVDDt+FoBetCbOWAUHgZn4k+CVeZgLmy7bx6aKeDbe3e8koOQ==} + '@tanstack/react-query@5.29.2': + resolution: {integrity: sha512-nyuWILR4u7H5moLGSiifLh8kIqQDLNOHGuSz0rcp+J75fNc8aQLyr5+I2JCHU3n+nJrTTW1ssgAD8HiKD7IFBQ==} peerDependencies: react: ^18.0.0 @@ -580,17 +580,17 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@20.12.2': - resolution: {integrity: sha512-zQ0NYO87hyN6Xrclcqp7f8ZbXNbRfoGWNcMvHTPQp9UUrwI0mI7XBz+cu7/W6/VClYo2g63B0cjull/srU7LgQ==} + '@types/node@20.12.7': + resolution: {integrity: sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==} '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - '@types/react-dom@18.2.23': - resolution: {integrity: sha512-ZQ71wgGOTmDYpnav2knkjr3qXdAFu0vsk8Ci5w3pGAIdj7/kKAyn+VsQDhXsmzzzepAiI9leWMmubXz690AI/A==} + '@types/react-dom@18.2.25': + resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==} - '@types/react@18.2.73': - resolution: {integrity: sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==} + '@types/react@18.2.79': + resolution: {integrity: sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==} '@types/swagger-schema-official@2.0.25': resolution: {integrity: sha512-T92Xav+Gf/Ik1uPW581nA+JftmjWPgskw/WBf4TJzxRG/SJ+DfNnNE+WuZ4mrXuzflQMqMkm1LSYjzYW7MB1Cg==} @@ -757,8 +757,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001603: - resolution: {integrity: sha512-iL2iSS0eDILMb9n5yKQoTBim9jMZ0Yrk8g0N9K7UzYyWnfIKzXBZD5ngpM37ZcL/cv0Mli8XtVMRYMQAfFpi5Q==} + caniuse-lite@1.0.30001612: + resolution: {integrity: sha512-lFgnZ07UhaCcsSZgWW0K5j4e69dK1u/ltrL9lTUiFOwNHs12S3UMIEYgBV0Z6C6hRDev7iRnMzzYmKabYdXF9g==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -829,8 +829,8 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - confbox@0.1.3: - resolution: {integrity: sha512-eH3ZxAihl1PhKfpr4VfEN6/vUd87fmgb6JkldHgg/YR6aEBhW63qUDgzP2Y6WM0UumdsYp5H3kibalXAdHfbgg==} + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} @@ -911,8 +911,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.4.722: - resolution: {integrity: sha512-5nLE0TWFFpZ80Crhtp4pIp8LXCztjYX41yUcV6b+bKR2PqzjskTMOOlBi1VjBHlvHwS+4gar7kNKOrsbsewEZQ==} + electron-to-chromium@1.4.745: + resolution: {integrity: sha512-tRbzkaRI5gbUn5DEvF0dV4TQbMZ5CLkWeTAXmpC9IrYT+GE+x76i9p+o3RJ5l9XmdQlI1pPhVtE9uNcJJ0G0EA==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1012,10 +1012,6 @@ packages: resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} engines: {node: '>=4'} - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - flatstr@1.0.12: resolution: {integrity: sha512-4zPxDyhCyiN2wIAtSLI6gc82/EjqZc1onI4Mz/l0pWrAlsSfYH/2ZIcU+e3oA2wDwbzIWNKwa23F8rh6+DRWkw==} @@ -1371,9 +1367,6 @@ packages: jsonc-parser@2.2.1: resolution: {integrity: sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==} - jsonc-parser@3.2.1: - resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==} - jsonpath-plus@7.2.0: resolution: {integrity: sha512-zBfiUPM5nD0YZSBT/o/fbCUlCcepMIdP0CJZxM1+KgA4f2T206f6VAg9e7mX35+KlMaIc5qXW34f3BnwJ3w+RA==} engines: {node: '>=12.0.0'} @@ -1692,8 +1685,8 @@ packages: resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} engines: {node: '>=4'} - pkg-types@1.0.3: - resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + pkg-types@1.1.0: + resolution: {integrity: sha512-/RpmvKdxKf8uILTtoOhAgf30wYbP2Qw+L9p3Rvshx1JZVX+XQNZQFjlbmGHEGIm4CkVPlSn+NXmIM8+9oWQaSA==} possible-typed-array-names@1.0.0: resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} @@ -1739,8 +1732,8 @@ packages: quick-format-unescaped@4.0.4: resolution: {integrity: sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==} - rc9@2.1.1: - resolution: {integrity: sha512-lNeOl38Ws0eNxpO3+wD1I9rkHGQyj1NU1jlzv4go2CtEnEQEUfqnIvZG7W+bC/aXdJ27n5x/yUjb6RoT9tko+Q==} + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} react-dom@18.2.0: resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} @@ -1795,8 +1788,8 @@ packages: engines: {node: '>=14'} hasBin: true - rollup@4.13.2: - resolution: {integrity: sha512-MIlLgsdMprDBXC+4hsPgzWUasLO9CE4zOkj/u6j+Z6j5A4zRY+CtiXAdJyPtgCsc42g658Aeh1DlrdVEJhsL2g==} + rollup@4.16.1: + resolution: {integrity: sha512-5CaD3MPDlPKfhqzRvWXK96G6ELJfPZNb3LHiZxTHgDdC6jvwfGz2E8nY+9g1ONk4ttHsK1WaFP19Js4PSr1E3g==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1836,6 +1829,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.6.0: + resolution: {integrity: sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==} + engines: {node: '>=10'} + hasBin: true + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -1981,8 +1979,8 @@ packages: resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} engines: {node: '>=8'} - tinybench@2.7.0: - resolution: {integrity: sha512-Qgayeb106x2o4hNzNjsZEfFziw8IbKqtbXBjVh7VIZfBxfD5M4gWtpyx5+YTae2gJ6Y6Dz/KLepiv16RFeQWNA==} + tinybench@2.8.0: + resolution: {integrity: sha512-1/eK7zUnIklz4JUUlL+658n58XO2hHLQfSk1Zf2LKieUjxidN16eKFEoDEfjHc3ohofSSqK3X5yO6VGb6iW8Lw==} tinypool@0.8.4: resolution: {integrity: sha512-i11VH5gS6IFeLY3gMBQ00/MmLncVP7JLXOw1vlgkytLmJK7QnEr7NXf0LBdxfmNPAeyetukOk0bOYrJrFGjYJQ==} @@ -2033,8 +2031,8 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} - typescript@5.4.3: - resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==} + typescript@5.4.5: + resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} engines: {node: '>=14.17'} hasBin: true @@ -2101,8 +2099,8 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite@5.2.7: - resolution: {integrity: sha512-k14PWOKLI6pMaSzAuGtT+Cf0YmIx12z9YGon39onaJNy8DLBfBJrzg9FQEmkAM5lpHBZs9wksWAsyF/HkpEwJA==} + vite@5.2.10: + resolution: {integrity: sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -2235,7 +2233,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@apidevtools/json-schema-ref-parser@11.5.4': + '@apidevtools/json-schema-ref-parser@11.5.5': dependencies: '@jsdevtools/ono': 7.1.3 '@types/json-schema': 7.0.15 @@ -2246,17 +2244,17 @@ snapshots: '@babel/highlight': 7.24.2 picocolors: 1.0.0 - '@babel/compat-data@7.24.1': {} + '@babel/compat-data@7.24.4': {} - '@babel/core@7.24.3': + '@babel/core@7.24.4': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.1 + '@babel/generator': 7.24.4 '@babel/helper-compilation-targets': 7.23.6 - '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.3) - '@babel/helpers': 7.24.1 - '@babel/parser': 7.24.1 + '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.4) + '@babel/helpers': 7.24.4 + '@babel/parser': 7.24.4 '@babel/template': 7.24.0 '@babel/traverse': 7.24.1 '@babel/types': 7.24.0 @@ -2268,7 +2266,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.24.1': + '@babel/generator@7.24.4': dependencies: '@babel/types': 7.24.0 '@jridgewell/gen-mapping': 0.3.5 @@ -2277,7 +2275,7 @@ snapshots: '@babel/helper-compilation-targets@7.23.6': dependencies: - '@babel/compat-data': 7.24.1 + '@babel/compat-data': 7.24.4 '@babel/helper-validator-option': 7.23.5 browserslist: 4.23.0 lru-cache: 5.1.1 @@ -2298,9 +2296,9 @@ snapshots: dependencies: '@babel/types': 7.24.0 - '@babel/helper-module-transforms@7.23.3(@babel/core@7.24.3)': + '@babel/helper-module-transforms@7.23.3(@babel/core@7.24.4)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.4 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-module-imports': 7.24.3 '@babel/helper-simple-access': 7.22.5 @@ -2323,7 +2321,7 @@ snapshots: '@babel/helper-validator-option@7.23.5': {} - '@babel/helpers@7.24.1': + '@babel/helpers@7.24.4': dependencies: '@babel/template': 7.24.0 '@babel/traverse': 7.24.1 @@ -2338,39 +2336,35 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.0.0 - '@babel/parser@7.24.1': - dependencies: - '@babel/types': 7.24.0 - '@babel/parser@7.24.4': dependencies: '@babel/types': 7.24.0 - '@babel/plugin-transform-react-jsx-self@7.24.1(@babel/core@7.24.3)': + '@babel/plugin-transform-react-jsx-self@7.24.1(@babel/core@7.24.4)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 - '@babel/plugin-transform-react-jsx-source@7.24.1(@babel/core@7.24.3)': + '@babel/plugin-transform-react-jsx-source@7.24.1(@babel/core@7.24.4)': dependencies: - '@babel/core': 7.24.3 + '@babel/core': 7.24.4 '@babel/helper-plugin-utils': 7.24.0 '@babel/template@7.24.0': dependencies: '@babel/code-frame': 7.24.2 - '@babel/parser': 7.24.1 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 '@babel/traverse@7.24.1': dependencies: '@babel/code-frame': 7.24.2 - '@babel/generator': 7.24.1 + '@babel/generator': 7.24.4 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.1 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 debug: 4.3.4 globals: 11.12.0 @@ -2458,14 +2452,14 @@ snapshots: '@faker-js/faker@6.3.1': {} - '@hey-api/openapi-ts@0.36.0(typescript@5.4.3)': + '@hey-api/openapi-ts@0.42.1(typescript@5.4.5)': dependencies: - '@apidevtools/json-schema-ref-parser': 11.5.4 + '@apidevtools/json-schema-ref-parser': 11.5.5 c12: 1.10.0 camelcase: 8.0.0 commander: 12.0.0 handlebars: 4.7.8 - typescript: 5.4.3 + typescript: 5.4.5 '@isaacs/cliui@8.0.2': dependencies: @@ -2516,49 +2510,52 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.13.2': + '@rollup/rollup-android-arm-eabi@4.16.1': + optional: true + + '@rollup/rollup-android-arm64@4.16.1': optional: true - '@rollup/rollup-android-arm64@4.13.2': + '@rollup/rollup-darwin-arm64@4.16.1': optional: true - '@rollup/rollup-darwin-arm64@4.13.2': + '@rollup/rollup-darwin-x64@4.16.1': optional: true - '@rollup/rollup-darwin-x64@4.13.2': + '@rollup/rollup-linux-arm-gnueabihf@4.16.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.13.2': + '@rollup/rollup-linux-arm-musleabihf@4.16.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.13.2': + '@rollup/rollup-linux-arm64-gnu@4.16.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.13.2': + '@rollup/rollup-linux-arm64-musl@4.16.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.13.2': + '@rollup/rollup-linux-powerpc64le-gnu@4.16.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.13.2': + '@rollup/rollup-linux-riscv64-gnu@4.16.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.13.2': + '@rollup/rollup-linux-s390x-gnu@4.16.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.13.2': + '@rollup/rollup-linux-x64-gnu@4.16.1': optional: true - '@rollup/rollup-linux-x64-musl@4.13.2': + '@rollup/rollup-linux-x64-musl@4.16.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.13.2': + '@rollup/rollup-win32-arm64-msvc@4.16.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.13.2': + '@rollup/rollup-win32-ia32-msvc@4.16.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.13.2': + '@rollup/rollup-win32-x64-msvc@4.16.1': optional: true '@sinclair/typebox@0.27.8': {} @@ -2734,11 +2731,11 @@ snapshots: '@stoplight/yaml-ast-parser': 0.0.50 tslib: 2.6.2 - '@tanstack/query-core@5.28.9': {} + '@tanstack/query-core@5.29.0': {} - '@tanstack/react-query@5.28.9(react@18.2.0)': + '@tanstack/react-query@5.29.2(react@18.2.0)': dependencies: - '@tanstack/query-core': 5.28.9 + '@tanstack/query-core': 5.29.0 react: 18.2.0 '@tootallnate/once@2.0.0': {} @@ -2752,7 +2749,7 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.24.1 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 @@ -2764,7 +2761,7 @@ snapshots: '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.24.1 + '@babel/parser': 7.24.4 '@babel/types': 7.24.0 '@types/babel__traverse@7.20.5': @@ -2777,17 +2774,17 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@20.12.2': + '@types/node@20.12.7': dependencies: undici-types: 5.26.5 '@types/prop-types@15.7.12': {} - '@types/react-dom@18.2.23': + '@types/react-dom@18.2.25': dependencies: - '@types/react': 18.2.73 + '@types/react': 18.2.79 - '@types/react@18.2.73': + '@types/react@18.2.79': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 @@ -2796,20 +2793,20 @@ snapshots: '@types/type-is@1.6.6': dependencies: - '@types/node': 20.12.2 + '@types/node': 20.12.7 - '@vitejs/plugin-react@4.2.1(vite@5.2.7(@types/node@20.12.2))': + '@vitejs/plugin-react@4.2.1(vite@5.2.10(@types/node@20.12.7))': dependencies: - '@babel/core': 7.24.3 - '@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.3) - '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.3) + '@babel/core': 7.24.4 + '@babel/plugin-transform-react-jsx-self': 7.24.1(@babel/core@7.24.4) + '@babel/plugin-transform-react-jsx-source': 7.24.1(@babel/core@7.24.4) '@types/babel__core': 7.20.5 react-refresh: 0.14.0 - vite: 5.2.7(@types/node@20.12.2) + vite: 5.2.10(@types/node@20.12.7) transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@1.5.0(vitest@1.5.0(@types/node@20.12.2))': + '@vitest/coverage-v8@1.5.0(vitest@1.5.0(@types/node@20.12.7))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -2824,7 +2821,7 @@ snapshots: std-env: 3.7.0 strip-literal: 2.1.0 test-exclude: 6.0.0 - vitest: 1.5.0(@types/node@20.12.2) + vitest: 1.5.0(@types/node@20.12.7) transitivePeerDependencies: - supports-color @@ -2967,15 +2964,15 @@ snapshots: browserslist@4.23.0: dependencies: - caniuse-lite: 1.0.30001603 - electron-to-chromium: 1.4.722 + caniuse-lite: 1.0.30001612 + electron-to-chromium: 1.4.745 node-releases: 2.0.14 update-browserslist-db: 1.0.13(browserslist@4.23.0) c12@1.10.0: dependencies: chokidar: 3.6.0 - confbox: 0.1.3 + confbox: 0.1.7 defu: 6.1.4 dotenv: 16.4.5 giget: 1.2.3 @@ -2984,8 +2981,8 @@ snapshots: ohash: 1.1.3 pathe: 1.1.2 perfect-debounce: 1.0.0 - pkg-types: 1.0.3 - rc9: 2.1.1 + pkg-types: 1.1.0 + rc9: 2.1.2 cac@6.7.14: {} @@ -3001,7 +2998,7 @@ snapshots: camelcase@8.0.0: {} - caniuse-lite@1.0.30001603: {} + caniuse-lite@1.0.30001612: {} caseless@0.12.0: {} @@ -3091,7 +3088,7 @@ snapshots: concat-map@0.0.1: {} - confbox@0.1.3: {} + confbox@0.1.7: {} consola@3.2.3: {} @@ -3171,7 +3168,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.4.722: {} + electron-to-chromium@1.4.745: {} emoji-regex@8.0.0: {} @@ -3338,8 +3335,6 @@ snapshots: dependencies: locate-path: 2.0.0 - flat@5.0.2: {} - flatstr@1.0.12: {} fnv-plus@1.3.1: {} @@ -3693,8 +3688,6 @@ snapshots: jsonc-parser@2.2.1: {} - jsonc-parser@3.2.1: {} - jsonpath-plus@7.2.0: {} liquid-json@0.3.1: {} @@ -3709,7 +3702,7 @@ snapshots: local-pkg@0.5.0: dependencies: mlly: 1.6.1 - pkg-types: 1.0.3 + pkg-types: 1.1.0 locate-path@2.0.0: dependencies: @@ -3750,7 +3743,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.5.4 + semver: 7.6.0 media-typer@0.3.0: {} @@ -3808,7 +3801,7 @@ snapshots: mkdirp@0.5.6: dependencies: - minimist: 1.2.8 + minimist: 1.2.6 mkdirp@1.0.4: {} @@ -3818,7 +3811,7 @@ snapshots: dependencies: acorn: 8.11.3 pathe: 1.1.2 - pkg-types: 1.0.3 + pkg-types: 1.1.0 ufo: 1.5.3 ms@2.1.2: {} @@ -3982,9 +3975,9 @@ snapshots: find-up: 2.1.0 load-json-file: 4.0.0 - pkg-types@1.0.3: + pkg-types@1.1.0: dependencies: - jsonc-parser: 3.2.1 + confbox: 0.1.7 mlly: 1.6.1 pathe: 1.1.2 @@ -4034,11 +4027,10 @@ snapshots: quick-format-unescaped@4.0.4: {} - rc9@2.1.1: + rc9@2.1.2: dependencies: defu: 6.1.4 destr: 2.0.3 - flat: 5.0.2 react-dom@18.2.0(react@18.2.0): dependencies: @@ -4093,25 +4085,26 @@ snapshots: dependencies: glob: 10.3.12 - rollup@4.13.2: + rollup@4.16.1: dependencies: '@types/estree': 1.0.5 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.13.2 - '@rollup/rollup-android-arm64': 4.13.2 - '@rollup/rollup-darwin-arm64': 4.13.2 - '@rollup/rollup-darwin-x64': 4.13.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.13.2 - '@rollup/rollup-linux-arm64-gnu': 4.13.2 - '@rollup/rollup-linux-arm64-musl': 4.13.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.13.2 - '@rollup/rollup-linux-riscv64-gnu': 4.13.2 - '@rollup/rollup-linux-s390x-gnu': 4.13.2 - '@rollup/rollup-linux-x64-gnu': 4.13.2 - '@rollup/rollup-linux-x64-musl': 4.13.2 - '@rollup/rollup-win32-arm64-msvc': 4.13.2 - '@rollup/rollup-win32-ia32-msvc': 4.13.2 - '@rollup/rollup-win32-x64-msvc': 4.13.2 + '@rollup/rollup-android-arm-eabi': 4.16.1 + '@rollup/rollup-android-arm64': 4.16.1 + '@rollup/rollup-darwin-arm64': 4.16.1 + '@rollup/rollup-darwin-x64': 4.16.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.16.1 + '@rollup/rollup-linux-arm-musleabihf': 4.16.1 + '@rollup/rollup-linux-arm64-gnu': 4.16.1 + '@rollup/rollup-linux-arm64-musl': 4.16.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.16.1 + '@rollup/rollup-linux-riscv64-gnu': 4.16.1 + '@rollup/rollup-linux-s390x-gnu': 4.16.1 + '@rollup/rollup-linux-x64-gnu': 4.16.1 + '@rollup/rollup-linux-x64-musl': 4.16.1 + '@rollup/rollup-win32-arm64-msvc': 4.16.1 + '@rollup/rollup-win32-ia32-msvc': 4.16.1 + '@rollup/rollup-win32-x64-msvc': 4.16.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -4149,6 +4142,10 @@ snapshots: dependencies: lru-cache: 6.0.0 + semver@7.6.0: + dependencies: + lru-cache: 6.0.0 + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -4314,7 +4311,7 @@ snapshots: glob: 7.2.3 minimatch: 3.1.2 - tinybench@2.7.0: {} + tinybench@2.8.0: {} tinypool@0.8.4: {} @@ -4374,7 +4371,7 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typescript@5.4.3: {} + typescript@5.4.5: {} ufo@1.5.3: {} @@ -4430,13 +4427,13 @@ snapshots: validate.io-number@1.0.3: {} - vite-node@1.5.0(@types/node@20.12.2): + vite-node@1.5.0(@types/node@20.12.7): dependencies: cac: 6.7.14 debug: 4.3.4 pathe: 1.1.2 picocolors: 1.0.0 - vite: 5.2.7(@types/node@20.12.2) + vite: 5.2.10(@types/node@20.12.7) transitivePeerDependencies: - '@types/node' - less @@ -4447,16 +4444,16 @@ snapshots: - supports-color - terser - vite@5.2.7(@types/node@20.12.2): + vite@5.2.10(@types/node@20.12.7): dependencies: esbuild: 0.20.2 postcss: 8.4.38 - rollup: 4.13.2 + rollup: 4.16.1 optionalDependencies: - '@types/node': 20.12.2 + '@types/node': 20.12.7 fsevents: 2.3.3 - vitest@1.5.0(@types/node@20.12.2): + vitest@1.5.0(@types/node@20.12.7): dependencies: '@vitest/expect': 1.5.0 '@vitest/runner': 1.5.0 @@ -4473,13 +4470,13 @@ snapshots: picocolors: 1.0.0 std-env: 3.7.0 strip-literal: 2.1.0 - tinybench: 2.7.0 + tinybench: 2.8.0 tinypool: 0.8.4 - vite: 5.2.7(@types/node@20.12.2) - vite-node: 1.5.0(@types/node@20.12.2) + vite: 5.2.10(@types/node@20.12.7) + vite-node: 1.5.0(@types/node@20.12.7) why-is-node-running: 2.2.2 optionalDependencies: - '@types/node': 20.12.2 + '@types/node': 20.12.7 transitivePeerDependencies: - less - lightningcss diff --git a/src/cli.mts b/src/cli.mts index 5bace6e..d7c808f 100644 --- a/src/cli.mts +++ b/src/cli.mts @@ -1,14 +1,29 @@ #!/usr/bin/env node import { generate } from "./generate.mjs"; import { Command, Option } from "commander"; -import { UserConfig } from "@hey-api/openapi-ts"; import { readFile } from "fs/promises"; import { dirname, join } from "path"; import { fileURLToPath } from "node:url"; +import { defaultOutputPath } from "./constants.mjs"; const program = new Command(); -export type LimitedUserConfig = Omit; +export type LimitedUserConfig = { + input: string; + output: string; + client?: "angular" | "axios" | "fetch" | "node" | "xhr"; + request?: string; + format?: "biome" | "prettier"; + lint?: "biome" | "eslint"; + operationId?: boolean; + serviceResponse?: "body" | "response"; + base?: string; + enums?: "javascript" | "typescript"; + useDateType?: boolean; + debug?: boolean; + noSchemas?: boolean; + schemaType?: "form" | "json"; +}; async function setupProgram() { const __filename = fileURLToPath(import.meta.url); @@ -25,21 +40,31 @@ async function setupProgram() { "-i, --input ", "OpenAPI specification, can be a path, url or string content (required)" ) - .option("-o, --output ", "Output directory", "openapi") + .option("-o, --output ", "Output directory", defaultOutputPath) .addOption( new Option("-c, --client ", "HTTP client to generate") .choices(["angular", "axios", "fetch", "node", "xhr"]) .default("fetch") ) .option("--request ", "Path to custom request file") - .option("--format", "Process output folder with formatter?") - .option("--lint", "Process output folder with linter?") + .addOption( + new Option( + "--format ", + "Process output folder with formatter?" + ).choices(["biome", "prettier"]) + ) + .addOption( + new Option( + "--lint ", + "Process output folder with linter?" + ).choices(["biome", "eslint"]) + ) .option("--operationId", "Use operation ID to generate operation names?") .addOption( new Option( "--serviceResponse ", "Define shape of returned value from service calls" - ).choices(["body", "generics", "response"]) + ).choices(["body", "response"]) ) .option( "--base ", @@ -55,6 +80,14 @@ async function setupProgram() { "--useDateType", "Use Date type instead of string for date types for models, this will not convert the data to a Date object" ) + .option("--debug", "Run in debug mode?") + .option("--noSchemas", "Disable generating JSON schemas") + .addOption( + new Option( + "--schemaType ", + "Type of JSON schema [Default: 'json']" + ).choices(["form", "json"]) + ) .parse(); const options = program.opts(); diff --git a/src/common.mts b/src/common.mts index 3d8acb2..2f28a5d 100644 --- a/src/common.mts +++ b/src/common.mts @@ -1,12 +1,15 @@ import { type PathLike } from "fs"; import { stat } from "fs/promises"; import ts from "typescript"; +import path from "path"; import { MethodDeclaration, - JSDoc, SourceFile, ParameterDeclaration, + ClassDeclaration, } from "ts-morph"; +import { LimitedUserConfig } from "./cli.mjs"; +import { queriesOutputPath, requestsOutputPath } from "./constants.mjs"; export const TData = ts.factory.createIdentifier("TData"); export const TError = ts.factory.createIdentifier("TError"); @@ -27,7 +30,11 @@ export const lowercaseFirstLetter = (str: string) => { }; export const getNameFromMethod = (method: MethodDeclaration) => { - return method.getName(); + const methodName = method.getName(); + if (!methodName) { + throw new Error("Method name not found"); + } + return methodName; }; export type MethodDescription = { @@ -36,7 +43,7 @@ export type MethodDescription = { method: MethodDeclaration; methodBlock: ts.Block; httpMethodName: string; - jsDoc: JSDoc[]; + jsDoc: string; isDeprecated: boolean; }; @@ -89,3 +96,77 @@ export function extractPropertiesFromObjectParam(param: ParameterDeclaration) { })); return paramNodes; } + +/** + * Replace the import("...") surrounding the type if there is one. + * This can happen when the type is imported from another file, but + * we are already importing all the types from that file. + * + * https://regex101.com/r/3DyHaQ/1 + * + * TODO: Replace with a more robust solution. + */ +export function getShortType(type: string) { + return type.replaceAll(/import\(".*"\)\./g, ""); +} + +export function getClassesFromService(node: SourceFile) { + const klasses = node.getClasses(); + + if (!klasses.length) { + throw new Error("No classes found"); + } + + return klasses.map((klass) => { + const className = klass.getName(); + if (!className) { + throw new Error("Class name not found"); + } + return { + className, + klass, + }; + }); +} + +export function getClassNameFromClassNode(klass: ClassDeclaration) { + const className = klass.getName(); + + if (!className) { + throw new Error("Class name not found"); + } + return className; +} + +export function formatOptions(options: LimitedUserConfig) { + // loop through properties on the options object + // if the property is a string of number then convert it to a number + // if the property is a string of boolean then convert it to a boolean + const formattedOptions = Object.entries(options).reduce( + (acc, [key, value]) => { + const typedKey = key as keyof LimitedUserConfig; + const typedValue = value as (typeof options)[keyof LimitedUserConfig]; + const parsedNumber = safeParseNumber(typedValue); + if (value === "true" || value === true) { + (acc as any)[typedKey] = true; + } else if (value === "false" || value === false) { + (acc as any)[typedKey] = false; + } else if (!isNaN(parsedNumber)) { + (acc as any)[typedKey] = parsedNumber; + } else { + (acc as any)[typedKey] = typedValue; + } + return acc; + }, + options + ); + return formattedOptions; +} + +export function buildRequestsOutputPath(outputPath: string) { + return path.join(outputPath, requestsOutputPath); +} + +export function buildQueriesOutputPath(outputPath: string) { + return path.join(outputPath, queriesOutputPath); +} diff --git a/src/constants.mts b/src/constants.mts index 853ed1e..1bed51e 100644 --- a/src/constants.mts +++ b/src/constants.mts @@ -1,3 +1,6 @@ export const defaultOutputPath = "openapi"; export const queriesOutputPath = "queries"; export const requestsOutputPath = "requests"; + +export const serviceFileName = "services.gen"; +export const modalsFileName = "types.gen"; diff --git a/src/createImports.mts b/src/createImports.mts index 9fc9ad0..62fa92a 100644 --- a/src/createImports.mts +++ b/src/createImports.mts @@ -1,6 +1,7 @@ import ts from "typescript"; import { posix } from "path"; import { Project } from "ts-morph"; +import { modalsFileName, serviceFileName } from "./constants.mjs"; const { join } = posix; @@ -13,11 +14,9 @@ export const createImports = ({ }) => { const modelsFile = project .getSourceFiles() - .find((sourceFile) => sourceFile.getFilePath().includes("models.ts")); + .find((sourceFile) => sourceFile.getFilePath().includes(modalsFileName)); - const serviceFile = project - .getSourceFiles() - .find((sourceFile) => sourceFile.getFilePath().includes("services.ts")); + const serviceFile = project.getSourceFileOrThrow(`${serviceFileName}.ts`); if (!modelsFile) { console.warn(` @@ -25,10 +24,6 @@ export const createImports = ({ This may be an error if \`.components.schemas\` or \`.components.parameters\` is defined in your OpenAPI input.`); } - if (!serviceFile) { - throw new Error("No service file found"); - } - const modelNames = modelsFile ? Array.from(modelsFile.getExportedDeclarations().keys()) : []; @@ -41,10 +36,6 @@ export const createImports = ({ name.endsWith(serviceEndName) ); - const serviceNamesData = serviceExports.filter((name) => - name.endsWith("Data") - ); - const imports = [ ts.factory.createImportDeclaration( undefined, @@ -106,17 +97,9 @@ export const createImports = ({ ts.factory.createIdentifier(serviceName) ) ), - // import all data objects from service file - ...serviceNamesData.map((dataName) => - ts.factory.createImportSpecifier( - false, - undefined, - ts.factory.createIdentifier(dataName) - ) - ), ]) ), - ts.factory.createStringLiteral(join("../requests")), + ts.factory.createStringLiteral(join("../requests", serviceFileName)), undefined ), ]; @@ -138,7 +121,7 @@ export const createImports = ({ ), ]) ), - ts.factory.createStringLiteral(join("../requests/models")), + ts.factory.createStringLiteral(join("../requests/", modalsFileName)), undefined ) ); diff --git a/src/createSource.mts b/src/createSource.mts index efa9c88..25ac103 100644 --- a/src/createSource.mts +++ b/src/createSource.mts @@ -132,20 +132,22 @@ export const createSource = async ({ const { commonSource, mainSource, suspenseSource, indexSource } = await createSourceFile(outputPath, serviceEndName); + const comment = `// generated with @7nohe/openapi-react-query-codegen@${version} \n\n`; + const commonResult = - `// generated with @7nohe/openapi-react-query-codegen@${version} \n` + + comment + printer.printNode(ts.EmitHint.Unspecified, commonSource, commonFile); const mainResult = - `// generated with @7nohe/openapi-react-query-codegen@${version} \n` + + comment + printer.printNode(ts.EmitHint.Unspecified, mainSource, queriesFile); const suspenseResult = - `// generated with @7nohe/openapi-react-query-codegen@${version} \n` + + comment + printer.printNode(ts.EmitHint.Unspecified, suspenseSource, suspenseFile); const indexResult = - `// generated with @7nohe/openapi-react-query-codegen@${version} \n` + + comment + printer.printNode(ts.EmitHint.Unspecified, indexSource, indexFile); return [ diff --git a/src/createUseMutation.mts b/src/createUseMutation.mts index a302536..e8deea0 100644 --- a/src/createUseMutation.mts +++ b/src/createUseMutation.mts @@ -8,6 +8,7 @@ import { capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromMethod, + getShortType, } from "./common.mjs"; import { addJSDocToNode } from "./util.mjs"; @@ -41,11 +42,9 @@ function generateAwaitedReturnType({ } export const createUseMutation = ({ - node, className, method, - jsDoc = [], - isDeprecated = false, + jsDoc, }: MethodDescription) => { const methodName = getNameFromMethod(method); const awaitedResponseDataType = generateAwaitedReturnType({ @@ -83,24 +82,13 @@ export const createUseMutation = ({ refParam.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, - // refParam.questionToken ?? refParam.initializer - // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) - // : refParam.questionToken, ts.factory.createTypeReferenceNode( - refParam.type.getText(param) + getShortType(refParam.type.getText(param)) ) ) ); }) .flat() - // return ts.factory.createPropertySignature( - // undefined, - // ts.factory.createIdentifier(param.getName()), - // param.compilerNode.questionToken ?? param.compilerNode.initializer - // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) - // : param.compilerNode.questionToken, - // param.compilerNode.type - // ); ) : ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword); @@ -262,7 +250,7 @@ export const createUseMutation = ({ ) ); - const hookWithJsDoc = addJSDocToNode(exportHook, node, isDeprecated, jsDoc); + const hookWithJsDoc = addJSDocToNode(exportHook, jsDoc); return { mutationResult, diff --git a/src/createUseQuery.mts b/src/createUseQuery.mts index 27086c3..a8e827a 100644 --- a/src/createUseQuery.mts +++ b/src/createUseQuery.mts @@ -5,6 +5,7 @@ import { capitalizeFirstLetter, extractPropertiesFromObjectParam, getNameFromMethod, + getShortType, queryKeyConstraint, queryKeyGenericType, TData, @@ -72,15 +73,6 @@ export const createApiResponseType = ({ }; }; -/** - * Replace the import("...") surrounding the type if there is one. - * This can happen when the type is imported from another file, but - * we are already importing all the types from that file. - */ -function getShortType(type: string) { - return type.replaceAll(/import\("[a-zA-Z\/\.-]*"\)\./g, ""); -} - export function getRequestParamFromMethod(method: MethodDeclaration) { if (!method.getParameters().length) { return null; @@ -122,9 +114,6 @@ export function getRequestParamFromMethod(method: MethodDeclaration) { refParam.optional ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) : undefined, - // param.hasQuestionToken() ?? param.getInitializer()?.compilerNode - // ? ts.factory.createToken(ts.SyntaxKind.QuestionToken) - // : param.getQuestionTokenNode()?.compilerNode, ts.factory.createTypeReferenceNode(refParam.typeName) ); }) @@ -426,11 +415,9 @@ function createQueryHook({ } export const createUseQuery = ({ - node, className, method, - jsDoc = [], - isDeprecated: deprecated = false, + jsDoc, }: MethodDescription) => { const methodName = getNameFromMethod(method); const queryKey = createQueryKeyFromMethod({ method, className }); @@ -461,13 +448,8 @@ export const createUseQuery = ({ className, }); - const hookWithJsDoc = addJSDocToNode(queryHook, node, deprecated, jsDoc); - const suspenseHookWithJsDoc = addJSDocToNode( - suspenseQueryHook, - node, - deprecated, - jsDoc - ); + const hookWithJsDoc = addJSDocToNode(queryHook, jsDoc); + const suspenseHookWithJsDoc = addJSDocToNode(suspenseQueryHook, jsDoc); const returnTypeExport = createReturnTypeExport({ className, diff --git a/src/format.mts b/src/format.mts new file mode 100644 index 0000000..b2dcbf3 --- /dev/null +++ b/src/format.mts @@ -0,0 +1,25 @@ +import { IndentationText, NewLineKind, Project, QuoteKind } from "ts-morph"; + +export const formatOutput = async (outputPath: string) => { + const project = new Project({ + skipAddingFilesFromTsConfig: true, + manipulationSettings: { + indentationText: IndentationText.TwoSpaces, + newLineKind: NewLineKind.LineFeed, + quoteKind: QuoteKind.Double, + usePrefixAndSuffixTextForRename: false, + useTrailingCommas: true, + }, + }); + + const sourceFiles = project.addSourceFilesAtPaths(`${outputPath}/**/*`); + + const tasks = sourceFiles.map((sourceFile) => { + sourceFile.formatText(); + sourceFile.fixMissingImports(); + sourceFile.organizeImports(); + return sourceFile.save(); + }); + + await Promise.all(tasks); +}; diff --git a/src/generate.mts b/src/generate.mts index 1df669d..0a19ed1 100644 --- a/src/generate.mts +++ b/src/generate.mts @@ -1,45 +1,43 @@ import { createClient, UserConfig } from "@hey-api/openapi-ts"; import { print } from "./print.mjs"; -import path from "path"; import { createSource } from "./createSource.mjs"; -import { defaultOutputPath, requestsOutputPath } from "./constants.mjs"; -import { safeParseNumber } from "./common.mjs"; +import { + buildQueriesOutputPath, + buildRequestsOutputPath, + formatOptions, +} from "./common.mjs"; +import { LimitedUserConfig } from "./cli.mjs"; +import { formatOutput } from "./format.mjs"; -export async function generate(options: UserConfig, version: string) { - const openApiOutputPath = path.join( - options.output ?? defaultOutputPath, - requestsOutputPath - ); +export async function generate(options: LimitedUserConfig, version: string) { + const openApiOutputPath = buildRequestsOutputPath(options.output); + const formattedOptions = formatOptions(options); - // loop through properties on the options object - // if the property is a string of number then convert it to a number - // if the property is a string of boolean then convert it to a boolean - const formattedOptions = Object.entries(options).reduce( - (acc, [key, value]) => { - const typedKey = key as keyof UserConfig; - const typedValue = value as (typeof options)[keyof UserConfig]; - const parsedNumber = safeParseNumber(typedValue); - if (value === "true" || value === true) { - (acc as any)[typedKey] = true; - } else if (value === "false" || value === false) { - (acc as any)[typedKey] = false; - } else if (!isNaN(parsedNumber)) { - (acc as any)[typedKey] = parsedNumber; - } else { - (acc as any)[typedKey] = typedValue; - } - return acc; - }, - options - ); const config: UserConfig = { - ...formattedOptions, + base: formattedOptions.base, + client: formattedOptions.client, + debug: formattedOptions.debug, + dryRun: false, + enums: formattedOptions.enums, + exportCore: true, + format: formattedOptions.format, + input: formattedOptions.input, + lint: formattedOptions.lint, output: openApiOutputPath, + request: formattedOptions.request, + schemas: { + export: !formattedOptions.noSchemas, + type: formattedOptions.schemaType, + }, + services: { + export: true, + response: formattedOptions.serviceResponse, + }, + types: { + dates: formattedOptions.useDateType, + export: true, + }, useOptions: true, - exportCore: true, - exportModels: true, - exportServices: true, - write: true, }; await createClient(config); const source = await createSource({ @@ -48,4 +46,6 @@ export async function generate(options: UserConfig, version: string) { serviceEndName: "Service", // we are hard coding this because changing the service end name was depreciated in @hey-api/openapi-ts }); await print(source, formattedOptions); + const queriesOutputPath = buildQueriesOutputPath(options.output); + await formatOutput(queriesOutputPath); } diff --git a/src/print.mts b/src/print.mts index f4ce6f1..cc2f4c8 100644 --- a/src/print.mts +++ b/src/print.mts @@ -1,17 +1,17 @@ import { mkdir, writeFile } from "fs/promises"; import path from "path"; -import { defaultOutputPath, queriesOutputPath } from "./constants.mjs"; +import { queriesOutputPath } from "./constants.mjs"; import { LimitedUserConfig } from "./cli.mjs"; -import { exists } from "./common.mjs"; +import { buildQueriesOutputPath, exists } from "./common.mjs"; async function printGeneratedTS( result: { name: string; content: string; }, - options: LimitedUserConfig + options: Pick ) { - const dir = path.join(options.output ?? defaultOutputPath, queriesOutputPath); + const dir = buildQueriesOutputPath(options.output); const dirExists = await exists(dir); if (!dirExists) { await mkdir(dir, { recursive: true }); @@ -24,9 +24,9 @@ export async function print( name: string; content: string; }[], - options: LimitedUserConfig + options: Pick ) { - const outputPath = options.output ?? defaultOutputPath; + const outputPath = options.output; const dirExists = await exists(outputPath); if (!dirExists) { await mkdir(outputPath); diff --git a/src/service.mts b/src/service.mts index 14239ed..c99c874 100644 --- a/src/service.mts +++ b/src/service.mts @@ -1,6 +1,11 @@ import ts from "typescript"; import { ClassDeclaration, Project, SourceFile } from "ts-morph"; -import { MethodDescription } from "./common.mjs"; +import { + MethodDescription, + getClassNameFromClassNode, + getClassesFromService, +} from "./common.mjs"; +import { serviceFileName } from "./constants.mjs"; export type Service = { node: SourceFile; @@ -14,7 +19,7 @@ export type Service = { export async function getServices(project: Project): Promise { const node = project .getSourceFiles() - .find((sourceFile) => sourceFile.getFilePath().includes("services.ts")); + .find((sourceFile) => sourceFile.getFilePath().includes(serviceFileName)); if (!node) { throw new Error("No service node found"); @@ -31,34 +36,6 @@ export async function getServices(project: Project): Promise { } satisfies Service; } -function getClassesFromService(node: SourceFile) { - const klasses = node.getClasses(); - - if (!klasses.length) { - throw new Error("No classes found"); - } - - return klasses.map((klass) => { - const className = klass.getName(); - if (!className) { - throw new Error("Class name not found"); - } - return { - className, - klass, - }; - }); -} - -function getClassNameFromClassNode(klass: ClassDeclaration) { - const className = klass.getName(); - - if (!className) { - throw new Error("Class name not found"); - } - return className; -} - function getMethodsFromService(node: SourceFile, klass: ClassDeclaration) { const methods = klass.getMethods(); if (!methods.length) { @@ -106,7 +83,13 @@ function getMethodsFromService(node: SourceFile, klass: ClassDeclaration) { }; const children = getAllChildren(method.compilerNode); - const jsDoc = method.getJsDocs().map((jsDoc) => jsDoc); + // get all JSDoc comments + // this should be an array of 1 or 0 + const jsDocs = children + .filter((c) => c.kind === ts.SyntaxKind.JSDoc) + .map((c) => c.getText(node.compilerNode)); + // get the first JSDoc comment + const jsDoc = jsDocs?.[0]; const isDeprecated = children.some( (c) => c.kind === ts.SyntaxKind.JSDocDeprecatedTag ); diff --git a/src/util.mts b/src/util.mts index 9de8417..8961d96 100644 --- a/src/util.mts +++ b/src/util.mts @@ -1,45 +1,28 @@ import ts from "typescript"; -import { JSDoc, SourceFile } from "ts-morph"; export function addJSDocToNode( node: T, - sourceFile: SourceFile, - deprecated: boolean, - jsDoc: JSDoc[] = [] + jsDoc: string | undefined ): T { - const deprecatedString = deprecated ? "@deprecated" : ""; + if (!jsDoc) { + return node; + } + // replace the first /** with * + // we do this because ts.addSyntheticLeadingComment will add /* to the beginning but we want /** + const removedFirstLine = jsDoc.trim().replace(/^\/\*\*/, "*"); + // remove the last */ because ts.addSyntheticLeadingComment will add it + const removedSecondLine = removedFirstLine.replace(/\*\/$/, ""); - const jsDocString = [deprecatedString] - .concat( - jsDoc.map((comment) => { - if (typeof comment === "string") { - return comment; - } - if (Array.isArray(comment)) { - return comment.map((c) => c.getText(sourceFile)).join("\n"); - } - return ""; - }) - ) - // remove empty lines - .filter(Boolean) - // trim - .map((comment) => comment.trim()) - // add * to each line - .map((comment) => `* ${comment}`) - // join lines - .join("\n") - // replace new lines with \n * - .replace(/\n/g, "\n * "); + const split = removedSecondLine.split("\n"); + const trimmed = split.map((line) => line.trim()); + const joined = trimmed.join("\n"); - const nodeWithJSDoc = jsDocString - ? ts.addSyntheticLeadingComment( - node, - ts.SyntaxKind.MultiLineCommentTrivia, - `*\n ${jsDocString}\n `, - true - ) - : node; + const nodeWithJSDoc = ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + joined, + true + ); return nodeWithJSDoc; } diff --git a/tests/__snapshots__/createSource.test.ts.snap b/tests/__snapshots__/createSource.test.ts.snap index 9df2e05..cfea912 100644 --- a/tests/__snapshots__/createSource.test.ts.snap +++ b/tests/__snapshots__/createSource.test.ts.snap @@ -2,6 +2,7 @@ exports[`createSource > createSource 1`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + export * from "./common"; export * from "./queries"; " @@ -9,9 +10,10 @@ export * from "./queries"; exports[`createSource > createSource 2`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + import { useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; -import { DefaultService, DefaultData } from "../requests"; -import { Pet, NewPet, Error } from "../requests/models"; +import { DefaultService } from "../requests/services.gen"; +import { Pet, NewPet, Error, $OpenApiTs } from "../requests/types.gen"; export type DefaultServiceFindPetsDefaultResponse = Awaited>; export type DefaultServiceFindPetsQueryResult = UseQueryResult; export const useDefaultServiceFindPetsKey = "DefaultServiceFindPets"; @@ -29,30 +31,74 @@ export type DefaultServiceDeletePetMutationResult = Awaited createSource 3`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + import * as Common from "./common"; import { useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; -import { DefaultService, DefaultData } from "../requests"; -import { Pet, NewPet, Error } from "../requests/models"; +import { DefaultService } from "../requests/services.gen"; +import { Pet, NewPet, Error, $OpenApiTs } from "../requests/types.gen"; +/** +* Returns all pets from the system that the user has access to +* Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. +* +* Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. +* +* @param data The data for the request. +* @param data.tags tags to filter by +* @param data.limit maximum number of results to return +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPets = = unknown[]>({ limit, tags }: { limit?: number; tags?: string[]; } = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: [Common.useDefaultServiceFindPetsKey, ...(queryKey ?? [{ limit, tags }])], queryFn: () => DefaultService.findPets({ limit, tags }) as TData, ...options }); /** - * @deprecated - */ +* @deprecated +* This path is not fully defined. +* @returns unknown unexpected error +* @throws ApiError +*/ export const useDefaultServiceGetNotDefined = = unknown[]>(queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: [Common.useDefaultServiceGetNotDefinedKey, ...(queryKey ?? [])], queryFn: () => DefaultService.getNotDefined() as TData, ...options }); +/** +* Returns a user based on a single ID, if the user does not have access to the pet +* @param data The data for the request. +* @param data.id ID of pet to fetch +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPetById = = unknown[]>({ id }: { id: number; }, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: [Common.useDefaultServiceFindPetByIdKey, ...(queryKey ?? [{ id }])], queryFn: () => DefaultService.findPetById({ id }) as TData, ...options }); +/** +* Creates a new pet in the store. Duplicates are allowed +* @param data The data for the request. +* @param data.requestBody Pet to add to the store +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceAddPet = (options?: Omit, "mutationFn">) => useMutation({ mutationFn: ({ requestBody }) => DefaultService.addPet({ requestBody }) as unknown as Promise, ...options }); /** - * @deprecated - */ +* @deprecated +* This path is not defined at all. +* @returns unknown unexpected error +* @throws ApiError +*/ export const useDefaultServicePostNotDefined = (options?: Omit, "mutationFn">) => useMutation({ mutationFn: () => DefaultService.postNotDefined() as unknown as Promise, ...options }); +/** +* deletes a single pet based on the ID supplied +* @param data The data for the request. +* @param data.id ID of pet to delete +* @returns Error unexpected error +* @returns void pet deleted +* @throws ApiError +*/ export const useDefaultServiceDeletePet = (options?: Omit, "mutationFn">) => useMutation createSource 4`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + import * as Common from "./common"; import { useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; -import { DefaultService, DefaultData } from "../requests"; -import { Pet, NewPet, Error } from "../requests/models"; +import { DefaultService } from "../requests/services.gen"; +import { Pet, NewPet, Error, $OpenApiTs } from "../requests/types.gen"; +/** +* Returns all pets from the system that the user has access to +* Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. +* +* Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. +* +* @param data The data for the request. +* @param data.tags tags to filter by +* @param data.limit maximum number of results to return +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPetsSuspense = = unknown[]>({ limit, tags }: { limit?: number; tags?: string[]; } = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: [Common.useDefaultServiceFindPetsKey, ...(queryKey ?? [{ limit, tags }])], queryFn: () => DefaultService.findPets({ limit, tags }) as TData, ...options }); /** - * @deprecated - */ +* @deprecated +* This path is not fully defined. +* @returns unknown unexpected error +* @throws ApiError +*/ export const useDefaultServiceGetNotDefinedSuspense = = unknown[]>(queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: [Common.useDefaultServiceGetNotDefinedKey, ...(queryKey ?? [])], queryFn: () => DefaultService.getNotDefined() as TData, ...options }); +/** +* Returns a user based on a single ID, if the user does not have access to the pet +* @param data The data for the request. +* @param data.id ID of pet to fetch +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPetByIdSuspense = = unknown[]>({ id }: { id: number; }, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: [Common.useDefaultServiceFindPetByIdKey, ...(queryKey ?? [{ id }])], queryFn: () => DefaultService.findPetById({ id }) as TData, ...options }); diff --git a/tests/__snapshots__/generate.test.ts.snap b/tests/__snapshots__/generate.test.ts.snap index 7674f7c..99bf153 100644 --- a/tests/__snapshots__/generate.test.ts.snap +++ b/tests/__snapshots__/generate.test.ts.snap @@ -2,9 +2,9 @@ exports[`generate > common.ts 1`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 -import { useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; -import { DefaultService, DefaultData } from "../requests"; -import { Pet, NewPet, Error } from "../requests/models"; + +import { UseQueryResult } from "@tanstack/react-query"; +import { DefaultService } from "../requests/services.gen"; export type DefaultServiceFindPetsDefaultResponse = Awaited>; export type DefaultServiceFindPetsQueryResult = UseQueryResult; export const useDefaultServiceFindPetsKey = "DefaultServiceFindPets"; @@ -22,6 +22,7 @@ export type DefaultServiceDeletePetMutationResult = Awaited index.ts 1`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + export * from "./common"; export * from "./queries"; " @@ -29,54 +30,122 @@ export * from "./queries"; exports[`generate > queries.ts 1`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + +import { UseMutationOptions, UseQueryOptions, useMutation, useQuery } from "@tanstack/react-query"; +import { DefaultService } from "../requests/services.gen"; +import { NewPet } from "../requests/types.gen"; import * as Common from "./common"; -import { useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; -import { DefaultService, DefaultData } from "../requests"; -import { Pet, NewPet, Error } from "../requests/models"; +/** +* Returns all pets from the system that the user has access to +* Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. +* +* Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. +* +* @param data The data for the request. +* @param data.tags tags to filter by +* @param data.limit maximum number of results to return +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPets = = unknown[]>({ limit, tags }: { - limit?: number; - tags?: string[]; + limit?: number; + tags?: string[]; } = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: [Common.useDefaultServiceFindPetsKey, ...(queryKey ?? [{ limit, tags }])], queryFn: () => DefaultService.findPets({ limit, tags }) as TData, ...options }); /** - * @deprecated - */ +* @deprecated +* This path is not fully defined. +* @returns unknown unexpected error +* @throws ApiError +*/ export const useDefaultServiceGetNotDefined = = unknown[]>(queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: [Common.useDefaultServiceGetNotDefinedKey, ...(queryKey ?? [])], queryFn: () => DefaultService.getNotDefined() as TData, ...options }); +/** +* Returns a user based on a single ID, if the user does not have access to the pet +* @param data The data for the request. +* @param data.id ID of pet to fetch +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPetById = = unknown[]>({ id }: { - id: number; + id: number; }, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useQuery({ queryKey: [Common.useDefaultServiceFindPetByIdKey, ...(queryKey ?? [{ id }])], queryFn: () => DefaultService.findPetById({ id }) as TData, ...options }); +/** +* Creates a new pet in the store. Duplicates are allowed +* @param data The data for the request. +* @param data.requestBody Pet to add to the store +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceAddPet = (options?: Omit, "mutationFn">) => useMutation({ mutationFn: ({ requestBody }) => DefaultService.addPet({ requestBody }) as unknown as Promise, ...options }); /** - * @deprecated - */ +* @deprecated +* This path is not defined at all. +* @returns unknown unexpected error +* @throws ApiError +*/ export const useDefaultServicePostNotDefined = (options?: Omit, "mutationFn">) => useMutation({ mutationFn: () => DefaultService.postNotDefined() as unknown as Promise, ...options }); +/** +* deletes a single pet based on the ID supplied +* @param data The data for the request. +* @param data.id ID of pet to delete +* @returns Error unexpected error +* @returns void pet deleted +* @throws ApiError +*/ export const useDefaultServiceDeletePet = (options?: Omit, "mutationFn">) => useMutation({ mutationFn: ({ id }) => DefaultService.deletePet({ id }) as unknown as Promise, ...options }); " `; exports[`generate > suspense.ts 1`] = ` "// generated with @7nohe/openapi-react-query-codegen@1.0.0 + +import { UseQueryOptions, useSuspenseQuery } from "@tanstack/react-query"; +import { DefaultService } from "../requests/services.gen"; import * as Common from "./common"; -import { useQuery, useSuspenseQuery, useMutation, UseQueryResult, UseQueryOptions, UseMutationOptions, UseMutationResult } from "@tanstack/react-query"; -import { DefaultService, DefaultData } from "../requests"; -import { Pet, NewPet, Error } from "../requests/models"; +/** +* Returns all pets from the system that the user has access to +* Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. +* +* Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. +* +* @param data The data for the request. +* @param data.tags tags to filter by +* @param data.limit maximum number of results to return +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPetsSuspense = = unknown[]>({ limit, tags }: { - limit?: number; - tags?: string[]; + limit?: number; + tags?: string[]; } = {}, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: [Common.useDefaultServiceFindPetsKey, ...(queryKey ?? [{ limit, tags }])], queryFn: () => DefaultService.findPets({ limit, tags }) as TData, ...options }); /** - * @deprecated - */ +* @deprecated +* This path is not fully defined. +* @returns unknown unexpected error +* @throws ApiError +*/ export const useDefaultServiceGetNotDefinedSuspense = = unknown[]>(queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: [Common.useDefaultServiceGetNotDefinedKey, ...(queryKey ?? [])], queryFn: () => DefaultService.getNotDefined() as TData, ...options }); +/** +* Returns a user based on a single ID, if the user does not have access to the pet +* @param data The data for the request. +* @param data.id ID of pet to fetch +* @returns Pet pet response +* @returns Error unexpected error +* @throws ApiError +*/ export const useDefaultServiceFindPetByIdSuspense = = unknown[]>({ id }: { - id: number; + id: number; }, queryKey?: TQueryKey, options?: Omit, "queryKey" | "queryFn">) => useSuspenseQuery({ queryKey: [Common.useDefaultServiceFindPetByIdKey, ...(queryKey ?? [{ id }])], queryFn: () => DefaultService.findPetById({ id }) as TData, ...options }); " `; diff --git a/tests/common.test.ts b/tests/common.test.ts index 36411ed..74e9642 100644 --- a/tests/common.test.ts +++ b/tests/common.test.ts @@ -1,5 +1,16 @@ -import { describe, expect, test } from "vitest"; -import { BuildCommonTypeName, capitalizeFirstLetter, lowercaseFirstLetter, safeParseNumber } from "../src/common.mts"; +import { describe, expect, test, vi } from "vitest"; +import { + BuildCommonTypeName, + capitalizeFirstLetter, + lowercaseFirstLetter, + safeParseNumber, + getShortType, + formatOptions, + getClassNameFromClassNode, + getClassesFromService, + getNameFromMethod, +} from "../src/common.mts"; +import { LimitedUserConfig } from "../src/cli.mts"; describe("common", () => { test("safeParseNumber", () => { @@ -26,9 +37,204 @@ describe("common", () => { expect(lowercased).toBe("testTestTest"); }); - test('buildCommonTypeName', () => { - const name = 'Name'; + test("buildCommonTypeName", () => { + const name = "Name"; const result = BuildCommonTypeName(name); - expect(result.escapedText).toBe('Common.Name'); + expect(result.escapedText).toBe("Common.Name"); + }); + + describe("getShortType", () => { + test("linux", () => { + const type = 'import("/my/absolute/path").MyType'; + const result = getShortType(type); + expect(result).toBe("MyType"); + }); + + test("no import", () => { + const type = "MyType"; + const result = getShortType(type); + expect(result).toBe("MyType"); + }); + + test("windows", () => { + const type = 'import("D:/types.gen").MyType'; + const result = getShortType(type); + expect(result).toBe("MyType"); + }); + + test("number", () => { + const type = 'import("C:/Projekt/test_3.0/path").MyType'; + const result = getShortType(type); + expect(result).toBe("MyType"); + }); + + test("underscore", () => { + const type = 'import("C:/Projekt/test_one/path").MyType'; + const result = getShortType(type); + expect(result).toBe("MyType"); + }); + + test("dash", () => { + const type = 'import("C:/Projekt/test-one/path").MyType'; + const result = getShortType(type); + expect(result).toBe("MyType"); + }); + }); + + test("formatOptions - converts string boolean to boolean (false)", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: "false" as any, + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(false); + }); + + test("formatOptions - converts string boolean to boolean (true)", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: "true" as any, + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(true); + }); + + test("formatOptions - converts string boolean to boolean (undefined)", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(undefined); + }); + + test("formatOptions - converts string number to number", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: "123" as any, + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(123); + }); + + test("formatOptions - leaves other values unchanged", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: "123" as any, + lint: "eslint", + }; + const formatted = formatOptions(options); + + expect(formatted.lint).toStrictEqual("eslint"); + }); + + test("formatOptions - converts string number to number", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: Number.NaN as any, + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(Number.NaN); + }); + + test("formatOptions - handle boolean true", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: true, + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(true); + }); + + test("formatOptions - handle boolean false", () => { + const options: LimitedUserConfig = { + input: "input", + output: "output", + debug: false, + }; + const formatted = formatOptions(options); + + expect(formatted.debug).toStrictEqual(false); + }); + + test("getClassNameFromClassNode - get's name", () => { + const klass = { + getName: () => "Test", + } as any; + const result = getClassNameFromClassNode(klass); + expect(result).toBe("Test"); + }); + + test("getClassNameFromClassNode - no name", () => { + const klass = { + getName: () => undefined, + } as any; + expect(() => getClassNameFromClassNode(klass)).toThrowError( + "Class name not found" + ); + }); + + test("getClassesFromService - returns class name and class", () => { + const klass = { + getName: vi.fn(() => "Test"), + }; + const node = { + getClasses: vi.fn(() => [klass]), + } as any; + const result = getClassesFromService(node); + expect(result).toStrictEqual([ + { + className: "Test", + klass, + }, + ]); + }); + + test("getClassesFromService - no classes", () => { + const node = { + getClasses: vi.fn(() => []), + } as any; + expect(() => getClassesFromService(node)).toThrowError("No classes found"); + }); + + test("getClassesFromService - no name", () => { + const klass = { + getName: vi.fn(() => undefined), + }; + const node = { + getClasses: vi.fn(() => [klass]), + } as any; + expect(() => getClassesFromService(node)).toThrowError( + "Class name not found" + ); + }); + + test("getNameFromMethod - get method name", () => { + const method = { + getName: vi.fn(() => "test"), + } as any; + const result = getNameFromMethod(method); + expect(result).toBe("test"); + }); + + test("getNameFromMethod - no method name", () => { + const method = { + getName: vi.fn(() => undefined), + } as any; + expect(() => getNameFromMethod(method)).toThrowError( + "Method name not found" + ); }); }); diff --git a/tests/createImports.test.ts b/tests/createImports.test.ts index 81139ac..b496693 100644 --- a/tests/createImports.test.ts +++ b/tests/createImports.test.ts @@ -22,8 +22,8 @@ describe(fileName, () => { const moduleNames = imports.map((i) => i.moduleSpecifier.text); expect(moduleNames).toStrictEqual([ "@tanstack/react-query", - "../requests", - "../requests/models", + "../requests/services.gen", + "../requests/types.gen", ]); await cleanOutputs(fileName); }); @@ -42,7 +42,11 @@ describe(fileName, () => { // @ts-ignore const moduleNames = imports.map((i) => i.moduleSpecifier.text); - expect(moduleNames).toStrictEqual(["@tanstack/react-query", "../requests"]); + expect(moduleNames).toStrictEqual([ + "@tanstack/react-query", + "../requests/services.gen", + "../requests/types.gen", + ]); await cleanOutputs(fileName); }); }); diff --git a/tests/generate.test.ts b/tests/generate.test.ts index 5e655be..eaba6a9 100644 --- a/tests/generate.test.ts +++ b/tests/generate.test.ts @@ -1,9 +1,9 @@ import { existsSync, readFileSync } from "node:fs"; import path from "node:path"; -import type { UserConfig } from "@hey-api/openapi-ts"; import { afterAll, beforeAll, describe, expect, test } from "vitest"; import { generate } from "../src/generate.mjs"; -import { rmdir } from "node:fs/promises"; +import { rm } from "node:fs/promises"; +import { LimitedUserConfig } from "../src/cli.mts"; const readOutput = (fileName: string) => { return readFileSync( @@ -14,18 +14,19 @@ const readOutput = (fileName: string) => { describe("generate", () => { beforeAll(async () => { - const options: UserConfig = { + const options: LimitedUserConfig = { input: path.join(__dirname, "inputs", "petstore.yaml"), output: path.join("tests", "outputs"), - lint: true, - format: false, + lint: "eslint", }; await generate(options, "1.0.0"); }); afterAll(async () => { if (existsSync(path.join(__dirname, "outputs"))) { - await rmdir(path.join(__dirname, "outputs"), { recursive: true }); + await rm(path.join(__dirname, "outputs"), { + recursive: true, + }); } }); diff --git a/tests/print.test.ts b/tests/print.test.ts new file mode 100644 index 0000000..e4b4d7f --- /dev/null +++ b/tests/print.test.ts @@ -0,0 +1,56 @@ +import { describe, test, expect, vi, beforeEach } from "vitest"; +import { print } from "../src/print.mjs"; +import * as common from "../src/common.mjs"; +import { mkdir, writeFile } from "fs/promises"; + +vi.mock("fs/promises", () => { + return { + mkdir: vi.fn(() => Promise.resolve()), + writeFile: vi.fn(() => Promise.resolve()), + }; +}); + +describe("print", () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + test("print - doesn't create folders if folders exist", async () => { + const exists = vi.spyOn(common, "exists"); + exists.mockImplementation(() => Promise.resolve(true)); + const result = await print( + [ + { + name: "test.ts", + content: 'console.log("test")', + }, + ], + { + output: "dist", + } + ); + expect(exists).toBeCalledTimes(2); + expect(result).toBeUndefined(); + expect(mkdir).toBeCalledTimes(0); + expect(writeFile).toBeCalledTimes(1); + }); + + test("print - creates folders if folders don't exist", async () => { + const exists = vi.spyOn(common, "exists"); + exists.mockImplementation(() => Promise.resolve(false)); + const result = await print( + [ + { + name: "test.ts", + content: 'console.log("test")', + }, + ], + { + output: "dist", + } + ); + expect(exists).toBeCalledTimes(2); + expect(result).toBeUndefined(); + expect(mkdir).toBeCalledTimes(2); + expect(writeFile).toBeCalledTimes(1); + }); +}); diff --git a/tests/utils.test.ts b/tests/utils.test.ts new file mode 100644 index 0000000..e85401e --- /dev/null +++ b/tests/utils.test.ts @@ -0,0 +1,268 @@ +import ts from "typescript"; +import { describe, expect, test } from "vitest"; +import { addJSDocToNode } from "../src/util.mts"; +import { Project } from "ts-morph"; + +describe("utils", () => { + test("addJSDocToNode - deprecated", () => { + const project = new Project({ + skipAddingFilesFromTsConfig: true, + }); + + // create class + const node = ts.factory.createClassDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + "TestClass", + undefined, + undefined, + [] + ); + // create file source + const tsFile = ts.createSourceFile( + "test.ts", + "", + ts.ScriptTarget.Latest, + false, + ts.ScriptKind.TS + ); + + // create source file + const tsSource = ts.factory.createSourceFile( + [node], + ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None + ); + + // print source file + const fileString = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, tsSource, tsFile); + + // create ts-morph source file + const sourceFile = project.createSourceFile("test.ts", fileString); + + if (!sourceFile) { + throw new Error("Source file not found"); + } + + // add jsdoc to node + const jsDoc = `/** + * @deprecated + * This is a test + * This is a test 2 + */`; + + const deprecated = true; + + // find class node + const foundNode = sourceFile.getClasses()[0]!; + + // add jsdoc to node + const nodeWithJSDoc = addJSDocToNode(foundNode.compilerNode, jsDoc); + + // print node + const nodetext = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, nodeWithJSDoc, tsFile); + + expect(nodetext).toMatchInlineSnapshot(` +"/** +* @deprecated +* This is a test +* This is a test 2 +*/ +export class TestClass { +}" + `); + }); + + test("addJSDocToNode - not deprecated", () => { + const project = new Project({ + skipAddingFilesFromTsConfig: true, + }); + + // create class + const node = ts.factory.createClassDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + "TestClass", + undefined, + undefined, + [] + ); + // create file source + const tsFile = ts.createSourceFile( + "test.ts", + "", + ts.ScriptTarget.Latest, + false, + ts.ScriptKind.TS + ); + + // create source file + const tsSource = ts.factory.createSourceFile( + [node], + ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None + ); + + // print source file + const fileString = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, tsSource, tsFile); + + // create ts-morph source file + const sourceFile = project.createSourceFile("test.ts", fileString); + + if (!sourceFile) { + throw new Error("Source file not found"); + } + + // add jsdoc to node + const jsDoc = `/** + * This is a test + * This is a test 2 + */`; + + // find class node + const foundNode = sourceFile.getClasses()[0]!; + + // add jsdoc to node + const nodeWithJSDoc = addJSDocToNode(foundNode.compilerNode, jsDoc); + + // print node + const nodetext = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, nodeWithJSDoc, tsFile); + + expect(nodetext).toMatchInlineSnapshot(` +"/** +* This is a test +* This is a test 2 +*/ +export class TestClass { +}" + `); + }); + + test("addJSDocToNode - does not add comment if no jsdoc", () => { + const project = new Project({ + skipAddingFilesFromTsConfig: true, + }); + + // create class + const node = ts.factory.createClassDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + "TestClass", + undefined, + undefined, + [] + ); + // create file source + const tsFile = ts.createSourceFile( + "test.ts", + "", + ts.ScriptTarget.Latest, + false, + ts.ScriptKind.TS + ); + + // create source file + const tsSource = ts.factory.createSourceFile( + [node], + ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None + ); + + // print source file + const fileString = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, tsSource, tsFile); + + // create ts-morph source file + const sourceFile = project.createSourceFile("test.ts", fileString); + + if (!sourceFile) { + throw new Error("Source file not found"); + } + + // add jsdoc to node + const jsDoc = undefined; + + // find class node + const foundNode = sourceFile.getClasses()[0]!; + + // add jsdoc to node + const nodeWithJSDoc = addJSDocToNode(foundNode.compilerNode, jsDoc); + + // print node + const nodetext = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, nodeWithJSDoc, tsFile); + + expect(nodetext).toMatchInlineSnapshot(` +"export class TestClass { +}" + `); + }); + + test("addJSDocToNode - adds comment if no jsdoc and deprecated true", () => { + const project = new Project({ + skipAddingFilesFromTsConfig: true, + }); + + // create class + const node = ts.factory.createClassDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + "TestClass", + undefined, + undefined, + [] + ); + // create file source + const tsFile = ts.createSourceFile( + "test.ts", + "", + ts.ScriptTarget.Latest, + false, + ts.ScriptKind.TS + ); + + // create source file + const tsSource = ts.factory.createSourceFile( + [node], + ts.factory.createToken(ts.SyntaxKind.EndOfFileToken), + ts.NodeFlags.None + ); + + // print source file + const fileString = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, tsSource, tsFile); + + // create ts-morph source file + const sourceFile = project.createSourceFile("test.ts", fileString); + + if (!sourceFile) { + throw new Error("Source file not found"); + } + + // add jsdoc to node + const jsDoc = undefined; + + // find class node + const foundNode = sourceFile.getClasses()[0]!; + + // add jsdoc to node + const nodeWithJSDoc = addJSDocToNode(foundNode.compilerNode, jsDoc); + + // print node + const nodetext = ts + .createPrinter() + .printNode(ts.EmitHint.Unspecified, nodeWithJSDoc, tsFile); + + expect(nodetext).toMatchInlineSnapshot(` +"export class TestClass { +}" + `); + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index e4609ef..22b6847 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -3,15 +3,15 @@ import { defineConfig } from "vitest/config"; export default defineConfig({ test: { coverage: { - reporter: ['text', 'json-summary', 'json', 'html'], - exclude: ['src/cli.mts', 'examples/**'], + reporter: ["text", "json-summary", "json", "html"], + exclude: ["src/cli.mts", "examples/**", "tests/**"], reportOnFailure: true, thresholds: { lines: 95, functions: 95, statements: 95, branches: 85, - } - } + }, + }, }, });