From 7c115e563459b9f0ab26234b01c0f52ff7a861c2 Mon Sep 17 00:00:00 2001 From: Philip Andrew Wee De Wang Date: Sat, 30 Sep 2023 10:50:26 +0800 Subject: [PATCH 1/6] Updated tests to actually check routes are working --- test/index.test.ts | 2 +- test/test-api-folder/app/api/route.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 test/test-api-folder/app/api/route.ts diff --git a/test/index.test.ts b/test/index.test.ts index ae9fee6c..d8ab1538 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -85,7 +85,7 @@ describe('withSwagger', () => { }, ], }, - apiFolder: 'pages/api', + apiFolder: 'test/test-api-folder/app/api', }), ).toMatchSnapshot(); }); diff --git a/test/test-api-folder/app/api/route.ts b/test/test-api-folder/app/api/route.ts new file mode 100644 index 00000000..b61e4bd4 --- /dev/null +++ b/test/test-api-folder/app/api/route.ts @@ -0,0 +1,14 @@ +import { NextResponse } from 'next/server'; + +/** + * @swagger + * /api/hello: + * get: + * description: Returns the hello world + * responses: + * 200: + * description: hello world + */ +export async function GET() { + return NextResponse.json({ data: 'sheesh' }); +} From a08a45975f75428f1033b420e76adcb1cf3367a4 Mon Sep 17 00:00:00 2001 From: Philip Andrew Wee De Wang Date: Sat, 30 Sep 2023 11:04:24 +0800 Subject: [PATCH 2/6] Added some comments to tell me what to do --- src/swagger.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/swagger.ts b/src/swagger.ts index bbfd8e5d..60ed367c 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -69,13 +69,22 @@ export function createSwaggerSpec({ }), }; + // Given the globs found, create a temporary folder with the files + // AST parse the temporary folder to add jsdoc comments where necessary + // send the temporary folder to swagger jsdoc instead + const options: Options = { apis, // Files containing annotations as above ...swaggerOptions, definition, }; + + console.log(options); + const spec = swaggerJsdoc(options); + // Delete the temporary folder + return spec; } From ecdb4887832b7fe7ae1b52583178ee7e252fb2a8 Mon Sep 17 00:00:00 2001 From: Philip Andrew Wee De Wang Date: Sat, 30 Sep 2023 11:12:18 +0800 Subject: [PATCH 3/6] Added with temp copied folder function --- src/helpers/index.ts | 1 + src/helpers/withTempCopiedFolder.ts | 31 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/helpers/index.ts create mode 100644 src/helpers/withTempCopiedFolder.ts diff --git a/src/helpers/index.ts b/src/helpers/index.ts new file mode 100644 index 00000000..22c2bffb --- /dev/null +++ b/src/helpers/index.ts @@ -0,0 +1 @@ +export * from './withTempCopiedFolder'; diff --git a/src/helpers/withTempCopiedFolder.ts b/src/helpers/withTempCopiedFolder.ts new file mode 100644 index 00000000..14890325 --- /dev/null +++ b/src/helpers/withTempCopiedFolder.ts @@ -0,0 +1,31 @@ +import { mkdtempSync, rmdirSync, readdirSync, copyFileSync } from 'fs'; +import { tmpdir } from 'os'; +import { join, resolve } from 'path'; + +/** DO NOT run two of these functions at once, I'm too lazy to handle that edge case */ +export const withTempCopiedFolder = async ( + folderToCopy: string, + callback: (tempFolderPath: string) => Promise +) => { + try { + folderToCopy = resolve(folderToCopy); // Make sure sourcePath is absolute + const tempFolderPath = mkdtempSync(join(tmpdir(), 'swagger-temp-')); + + const files = readdirSync(folderToCopy); + + // Copy files from sourcePath to tempFolderPath + for (const file of files) { + const src = join(folderToCopy, file); + const dest = join(tempFolderPath, file); + copyFileSync(src, dest); + } + + // Run the callback with the temporary folder path + await callback(tempFolderPath); + + // Cleanup temporary folder + rmdirSync(tempFolderPath, { recursive: true }); + } catch (error) { + console.error('Error:', error); + } +}; From aaf4ebfd7904318bfcd5f568590a2bb0d973277e Mon Sep 17 00:00:00 2001 From: Philip Andrew Wee De Wang Date: Sun, 1 Oct 2023 11:21:41 +0800 Subject: [PATCH 4/6] Now able to get routes and code --- package.json | 1 + pnpm-lock.yaml | 98 ++++++++++++++++++++++------- src/helpers/getRoutesAndCode.ts | 21 +++++++ src/helpers/withTempCopiedFolder.ts | 31 --------- src/swagger.ts | 13 ++-- test/index.test.ts | 15 +++-- 6 files changed, 113 insertions(+), 66 deletions(-) create mode 100644 src/helpers/getRoutesAndCode.ts delete mode 100644 src/helpers/withTempCopiedFolder.ts diff --git a/package.json b/package.json index e4d502c6..3baf2b66 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "dependencies": { "@types/swagger-jsdoc": "6.0.1", "cleye": "1.3.2", + "glob": "^10.3.10", "isarray": "2.0.5", "swagger-jsdoc": "6.2.8" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72a3bf9e..1f5e685d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: cleye: specifier: 1.3.2 version: 1.3.2 + glob: + specifier: ^10.3.10 + version: 10.3.10 isarray: specifier: 2.0.5 version: 2.0.5 @@ -613,6 +616,18 @@ packages: - supports-color dev: true + /@isaacs/cliui@8.0.2: + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + dependencies: + string-width: 5.1.2 + string-width-cjs: /string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: /strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: /wrap-ansi@7.0.0 + dev: false + /@istanbuljs/schema@0.1.3: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -778,6 +793,13 @@ packages: fastq: 1.15.0 dev: true + /@pkgjs/parseargs@0.11.0: + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + requiresBuild: true + dev: false + optional: true + /@polka/url@1.0.0-next.21: resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==} dev: true @@ -1218,12 +1240,10 @@ packages: /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - dev: true /ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: true /ansi-sequence-parser@1.1.1: resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==} @@ -1241,7 +1261,6 @@ packages: engines: {node: '>=8'} dependencies: color-convert: 2.0.1 - dev: true /ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} @@ -1251,7 +1270,6 @@ packages: /ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: true /anymatch@3.1.3: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} @@ -1398,7 +1416,6 @@ packages: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} dependencies: balanced-match: 1.0.2 - dev: true /braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} @@ -1608,7 +1625,6 @@ packages: engines: {node: '>=7.0.0'} dependencies: color-name: 1.1.4 - dev: true /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} @@ -1616,7 +1632,6 @@ packages: /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - dev: true /colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} @@ -1661,7 +1676,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -1765,7 +1779,6 @@ packages: /eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true /electron-to-chromium@1.4.503: resolution: {integrity: sha512-LF2IQit4B0VrUHFeQkWhZm97KuJSGF2WJqq1InpY+ECpFRkXd8yTIaTtJxsO0OKDmiBYwWqcrNaXOurn2T2wiA==} @@ -1773,11 +1786,9 @@ packages: /emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true /emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - dev: true /es-abstract@1.22.1: resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} @@ -2348,6 +2359,14 @@ packages: signal-exit: 3.0.7 dev: true + /foreground-child@3.1.1: + resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} + engines: {node: '>=14'} + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + dev: false + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -2440,6 +2459,18 @@ packages: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} dev: true + /glob@10.3.10: + resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.1.1 + jackspeak: 2.3.6 + minimatch: 9.0.3 + minipass: 7.0.4 + path-scurry: 1.10.1 + dev: false + /glob@7.1.6: resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==} dependencies: @@ -2725,7 +2756,6 @@ packages: /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} - dev: true /is-fullwidth-code-point@4.0.0: resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} @@ -2853,7 +2883,6 @@ packages: /isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - dev: true /istanbul-lib-coverage@3.2.0: resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} @@ -2898,6 +2927,15 @@ packages: reflect.getprototypeof: 1.0.3 dev: true + /jackspeak@2.3.6: + resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==} + engines: {node: '>=14'} + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + dev: false + /jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} dev: true @@ -3100,6 +3138,11 @@ packages: get-func-name: 2.0.0 dev: true + /lru-cache@10.0.1: + resolution: {integrity: sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==} + engines: {node: 14 || >=16.14} + dev: false + /lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} dependencies: @@ -3188,12 +3231,16 @@ packages: engines: {node: '>=16 || 14 >=14.17'} dependencies: brace-expansion: 2.0.1 - dev: true /minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true + /minipass@7.0.4: + resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==} + engines: {node: '>=16 || 14 >=14.17'} + dev: false + /mlly@1.4.1: resolution: {integrity: sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==} dependencies: @@ -3470,7 +3517,6 @@ packages: /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} - dev: true /path-key@4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} @@ -3481,6 +3527,14 @@ packages: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} dev: true + /path-scurry@1.10.1: + resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + lru-cache: 10.0.1 + minipass: 7.0.4 + dev: false + /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -3825,12 +3879,10 @@ packages: engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 - dev: true /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - dev: true /shiki@0.14.3: resolution: {integrity: sha512-U3S/a+b0KS+UkTyMjoNojvTgrBHjgp7L6ovhFVZsXmBGnVdQ4K4U9oK0z63w538S91ATngv1vXigHCSWOwnr+g==} @@ -3857,6 +3909,11 @@ packages: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: false + /sirv@2.0.3: resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} engines: {node: '>= 10'} @@ -3949,7 +4006,6 @@ packages: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - dev: true /string-width@5.1.2: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} @@ -3958,7 +4014,6 @@ packages: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true /string.prototype.matchall@4.0.8: resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} @@ -4003,14 +4058,12 @@ packages: engines: {node: '>=8'} dependencies: ansi-regex: 5.0.1 - dev: true /strip-ansi@7.1.0: resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} engines: {node: '>=12'} dependencies: ansi-regex: 6.0.1 - dev: true /strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} @@ -4525,7 +4578,6 @@ packages: hasBin: true dependencies: isexe: 2.0.0 - dev: true /why-is-node-running@2.2.2: resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} @@ -4552,7 +4604,6 @@ packages: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true /wrap-ansi@8.1.0: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} @@ -4561,7 +4612,6 @@ packages: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} diff --git a/src/helpers/getRoutesAndCode.ts b/src/helpers/getRoutesAndCode.ts new file mode 100644 index 00000000..5e34f4f5 --- /dev/null +++ b/src/helpers/getRoutesAndCode.ts @@ -0,0 +1,21 @@ +import { sync } from 'glob'; +import fs from 'fs'; +import { join, sep } from 'path'; + +export function getRoutesAndCode(rootDir: string): Record { + const routes: Record = {}; + + const files = sync('**/*.{js,ts}', { cwd: rootDir }); + + for (const filePath of files) { + const route = filePath + .replace(/\.[jt]s$/, '') + .split(sep) + .join('/'); + const resolvedPath = join(rootDir, filePath); + const code = fs.readFileSync(resolvedPath, 'utf-8'); + routes[route] = code; + } + + return routes; +} diff --git a/src/helpers/withTempCopiedFolder.ts b/src/helpers/withTempCopiedFolder.ts deleted file mode 100644 index 14890325..00000000 --- a/src/helpers/withTempCopiedFolder.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { mkdtempSync, rmdirSync, readdirSync, copyFileSync } from 'fs'; -import { tmpdir } from 'os'; -import { join, resolve } from 'path'; - -/** DO NOT run two of these functions at once, I'm too lazy to handle that edge case */ -export const withTempCopiedFolder = async ( - folderToCopy: string, - callback: (tempFolderPath: string) => Promise -) => { - try { - folderToCopy = resolve(folderToCopy); // Make sure sourcePath is absolute - const tempFolderPath = mkdtempSync(join(tmpdir(), 'swagger-temp-')); - - const files = readdirSync(folderToCopy); - - // Copy files from sourcePath to tempFolderPath - for (const file of files) { - const src = join(folderToCopy, file); - const dest = join(tempFolderPath, file); - copyFileSync(src, dest); - } - - // Run the callback with the temporary folder path - await callback(tempFolderPath); - - // Cleanup temporary folder - rmdirSync(tempFolderPath, { recursive: true }); - } catch (error) { - console.error('Error:', error); - } -}; diff --git a/src/swagger.ts b/src/swagger.ts index 60ed367c..8fd54995 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -1,6 +1,7 @@ import { type NextApiRequest, type NextApiResponse } from 'next'; import { join } from 'path'; import swaggerJsdoc, { type OAS3Definition, type Options } from 'swagger-jsdoc'; +import { getRoutesAndCode } from './helpers/getRoutesAndCode'; export type SwaggerOptions = Options & { apiFolder?: string; @@ -54,7 +55,11 @@ export function createSwaggerSpec({ ]; }); - // Append base path server element to server array + // For each of the api folders, parse as AST and get the route and possible return types + const routesPathToCode = getRoutesAndCode(apiFolder); + console.log(routesPathToCode); + // Convert that data and append it to the swagger options + // Conditions: basePath is specified. Server array is not defined. const definition = { ...swaggerOptions.definition, @@ -69,18 +74,12 @@ export function createSwaggerSpec({ }), }; - // Given the globs found, create a temporary folder with the files - // AST parse the temporary folder to add jsdoc comments where necessary - // send the temporary folder to swagger jsdoc instead - const options: Options = { apis, // Files containing annotations as above ...swaggerOptions, definition, }; - console.log(options); - const spec = swaggerJsdoc(options); // Delete the temporary folder diff --git a/test/index.test.ts b/test/index.test.ts index d8ab1538..babf0381 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -13,7 +13,7 @@ describe('withSwagger', () => { version: '0.1.0', }, }, - }), + }) ).toMatchSnapshot(); }); @@ -42,7 +42,7 @@ describe('withSwagger', () => { ], }, apiFolder: 'pages/api', - }), + }) ).toMatchSnapshot(); }); @@ -85,8 +85,15 @@ describe('withSwagger', () => { }, ], }, - apiFolder: 'test/test-api-folder/app/api', - }), + apiFolder: 'test/test-api-folder/app', + }) ).toMatchSnapshot(); }); + + it.todo('Should work for normal next.js paths (App Router)'); + it.todo('Should work for dynamic next.js paths (App Router)'); + + it.todo( + "Should warn user when using path router cos I'm too lazy to implement that shit" + ); }); From d1e70114ae39a9615cf2436830404fa1046b6dff Mon Sep 17 00:00:00 2001 From: Philip Andrew Wee De Wang Date: Sun, 1 Oct 2023 11:46:45 +0800 Subject: [PATCH 5/6] Now uses doctrine to parse jsdoc comments --- package.json | 6 + pnpm-lock.yaml | 159 ++++++++++++++++---------- src/helpers/codeToAST.ts | 10 ++ src/swagger.ts | 22 +++- test/test-api-folder/app/api/route.ts | 9 +- 5 files changed, 140 insertions(+), 66 deletions(-) create mode 100644 src/helpers/codeToAST.ts diff --git a/package.json b/package.json index 3baf2b66..0c7363b7 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,12 @@ "package.json": "sort-package-json" }, "dependencies": { + "@babel/parser": "^7.23.0", + "@babel/traverse": "^7.23.0", + "@babel/types": "^7.23.0", "@types/swagger-jsdoc": "6.0.1", "cleye": "1.3.2", + "doctrine": "^3.0.0", "glob": "^10.3.10", "isarray": "2.0.5", "swagger-jsdoc": "6.2.8" @@ -56,6 +60,8 @@ "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "4.1.0", "@skypack/package-check": "0.2.2", + "@types/babel__traverse": "^7.20.2", + "@types/doctrine": "^0.0.7", "@types/node": "20.6.4", "@typescript-eslint/eslint-plugin": "6.7.2", "@typescript-eslint/parser": "6.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f5e685d..652c7591 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,12 +5,24 @@ settings: excludeLinksFromLockfile: false dependencies: + '@babel/parser': + specifier: ^7.23.0 + version: 7.23.0 + '@babel/traverse': + specifier: ^7.23.0 + version: 7.23.0 + '@babel/types': + specifier: ^7.23.0 + version: 7.23.0 '@types/swagger-jsdoc': specifier: 6.0.1 version: 6.0.1 cleye: specifier: 1.3.2 version: 1.3.2 + doctrine: + specifier: ^3.0.0 + version: 3.0.0 glob: specifier: ^10.3.10 version: 10.3.10 @@ -28,6 +40,12 @@ devDependencies: '@skypack/package-check': specifier: 0.2.2 version: 0.2.2 + '@types/babel__traverse': + specifier: ^7.20.2 + version: 7.20.2 + '@types/doctrine': + specifier: ^0.0.7 + version: 0.0.7 '@types/node': specifier: 20.6.4 version: 20.6.4 @@ -147,6 +165,13 @@ packages: chalk: 2.4.2 dev: true + /@babel/code-frame@7.22.13: + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/highlight': 7.22.20 + chalk: 2.4.2 + /@babel/compat-data@7.22.9: resolution: {integrity: sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==} engines: {node: '>=6.9.0'} @@ -162,10 +187,10 @@ packages: '@babel/helper-compilation-targets': 7.22.10 '@babel/helper-module-transforms': 7.22.9(@babel/core@7.22.11) '@babel/helpers': 7.22.11 - '@babel/parser': 7.22.11 + '@babel/parser': 7.23.0 '@babel/template': 7.22.5 - '@babel/traverse': 7.22.11 - '@babel/types': 7.22.11 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 convert-source-map: 1.9.0 debug: 4.3.4 gensync: 1.0.0-beta.2 @@ -179,12 +204,21 @@ packages: resolution: {integrity: sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 + '@babel/types': 7.23.0 '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.19 jsesc: 2.5.2 dev: true + /@babel/generator@7.23.0: + resolution: {integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/types': 7.23.0 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.19 + jsesc: 2.5.2 + /@babel/helper-compilation-targets@7.22.10: resolution: {integrity: sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==} engines: {node: '>=6.9.0'} @@ -196,31 +230,33 @@ packages: semver: 6.3.1 dev: true + /@babel/helper-environment-visitor@7.22.20: + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + /@babel/helper-environment-visitor@7.22.5: resolution: {integrity: sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==} engines: {node: '>=6.9.0'} dev: true - /@babel/helper-function-name@7.22.5: - resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==} + /@babel/helper-function-name@7.23.0: + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/template': 7.22.5 - '@babel/types': 7.22.11 - dev: true + '@babel/template': 7.22.15 + '@babel/types': 7.23.0 /@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 - dev: true + '@babel/types': 7.23.0 /@babel/helper-module-imports@7.22.5: resolution: {integrity: sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 + '@babel/types': 7.23.0 dev: true /@babel/helper-module-transforms@7.22.9(@babel/core@7.22.11): @@ -241,20 +277,22 @@ packages: resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 + '@babel/types': 7.23.0 dev: true /@babel/helper-split-export-declaration@7.22.6: resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} engines: {node: '>=6.9.0'} dependencies: - '@babel/types': 7.22.11 - dev: true + '@babel/types': 7.23.0 /@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} engines: {node: '>=6.9.0'} - dev: true + + /@babel/helper-validator-identifier@7.22.20: + resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} + engines: {node: '>=6.9.0'} /@babel/helper-validator-identifier@7.22.5: resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} @@ -271,8 +309,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/template': 7.22.5 - '@babel/traverse': 7.22.11 - '@babel/types': 7.22.11 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 transitivePeerDependencies: - supports-color dev: true @@ -286,13 +324,20 @@ packages: js-tokens: 4.0.0 dev: true - /@babel/parser@7.22.11: - resolution: {integrity: sha512-R5zb8eJIBPJriQtbH/htEQy4k7E2dHWlD2Y2VT07JCzwYZHBxV5ZYtM0UhXSNMT74LyxuM+b1jdL7pSesXbC/g==} + /@babel/highlight@7.22.20: + resolution: {integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/helper-validator-identifier': 7.22.20 + chalk: 2.4.2 + js-tokens: 4.0.0 + + /@babel/parser@7.23.0: + resolution: {integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==} engines: {node: '>=6.0.0'} hasBin: true dependencies: - '@babel/types': 7.22.11 - dev: true + '@babel/types': 7.23.0 /@babel/runtime@7.22.11: resolution: {integrity: sha512-ee7jVNlWN09+KftVOu9n7S8gQzD/Z6hN/I8VBRXW4P1+Xe7kJGXMwu8vds4aGIMHZnNbdpSWCfZZtinytpcAvA==} @@ -301,41 +346,47 @@ packages: regenerator-runtime: 0.14.0 dev: true + /@babel/template@7.22.15: + resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} + engines: {node: '>=6.9.0'} + dependencies: + '@babel/code-frame': 7.22.13 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 + /@babel/template@7.22.5: resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==} engines: {node: '>=6.9.0'} dependencies: '@babel/code-frame': 7.22.10 - '@babel/parser': 7.22.11 - '@babel/types': 7.22.11 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 dev: true - /@babel/traverse@7.22.11: - resolution: {integrity: sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==} + /@babel/traverse@7.23.0: + resolution: {integrity: sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==} engines: {node: '>=6.9.0'} dependencies: - '@babel/code-frame': 7.22.10 - '@babel/generator': 7.22.10 - '@babel/helper-environment-visitor': 7.22.5 - '@babel/helper-function-name': 7.22.5 + '@babel/code-frame': 7.22.13 + '@babel/generator': 7.23.0 + '@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.22.11 - '@babel/types': 7.22.11 + '@babel/parser': 7.23.0 + '@babel/types': 7.23.0 debug: 4.3.4 globals: 11.12.0 transitivePeerDependencies: - supports-color - dev: true - /@babel/types@7.22.11: - resolution: {integrity: sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==} + /@babel/types@7.23.0: + resolution: {integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==} engines: {node: '>=6.9.0'} dependencies: '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.5 + '@babel/helper-validator-identifier': 7.22.20 to-fast-properties: 2.0.0 - dev: true /@bcoe/v8-coverage@0.2.3: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -607,9 +658,9 @@ packages: dependencies: '@babel/core': 7.22.11 '@babel/generator': 7.22.10 - '@babel/parser': 7.22.11 - '@babel/traverse': 7.22.11 - '@babel/types': 7.22.11 + '@babel/parser': 7.23.0 + '@babel/traverse': 7.23.0 + '@babel/types': 7.23.0 prettier: 3.0.3 semver: 7.5.4 transitivePeerDependencies: @@ -647,28 +698,23 @@ packages: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.19 - dev: true /@jridgewell/resolve-uri@3.1.1: resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} - dev: true /@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - dev: true /@jridgewell/trace-mapping@0.3.19: resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 - dev: true /@jsdevtools/ono@7.1.3: resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -928,6 +974,12 @@ packages: tslib: 2.6.2 dev: true + /@types/babel__traverse@7.20.2: + resolution: {integrity: sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==} + dependencies: + '@babel/types': 7.23.0 + dev: true + /@types/chai-subset@1.3.3: resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} dependencies: @@ -938,6 +990,10 @@ packages: resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} dev: true + /@types/doctrine@0.0.7: + resolution: {integrity: sha512-M7BAOsy3VDlJktK4ZTg9eiI7J/9lzECY1H8Kes+U/dCu8fOc6067Fn25XBjVCfPc2Vi1Tv8b6sM7AoDGfLzXaA==} + dev: true + /@types/estree@1.0.1: resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} dev: true @@ -1254,7 +1310,6 @@ packages: engines: {node: '>=4'} dependencies: color-convert: 1.9.3 - dev: true /ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} @@ -1521,7 +1576,6 @@ packages: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - dev: true /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1618,7 +1672,6 @@ packages: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} dependencies: color-name: 1.1.3 - dev: true /color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -1628,7 +1681,6 @@ packages: /color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} - dev: true /color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} @@ -1702,7 +1754,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: true /decamelize@1.2.0: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} @@ -1916,7 +1967,6 @@ packages: /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - dev: true /escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} @@ -2507,7 +2557,6 @@ packages: /globals@11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} - dev: true /globals@13.21.0: resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} @@ -2567,7 +2616,6 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} - dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -2942,7 +2990,6 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - dev: true /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} @@ -2954,7 +3001,6 @@ packages: resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} engines: {node: '>=4'} hasBin: true - dev: true /json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} @@ -3257,7 +3303,6 @@ packages: /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - dev: true /ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4109,7 +4154,6 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 - dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -4192,7 +4236,6 @@ packages: /to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} - dev: true /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} diff --git a/src/helpers/codeToAST.ts b/src/helpers/codeToAST.ts new file mode 100644 index 00000000..c559a39a --- /dev/null +++ b/src/helpers/codeToAST.ts @@ -0,0 +1,10 @@ +import * as parser from '@babel/parser'; +import type { File } from '@babel/types'; + +export function codeToAst(code: string): File { + // I have no idea what eslint's problem is, the return type is clearly not any. Incompetent OSS contributors to eslint? Or am I incompetent? + return parser.parse(code, { + sourceType: 'module', + plugins: ['jsx', 'typescript'], + }); +} diff --git a/src/swagger.ts b/src/swagger.ts index 8fd54995..478014c3 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -2,6 +2,9 @@ import { type NextApiRequest, type NextApiResponse } from 'next'; import { join } from 'path'; import swaggerJsdoc, { type OAS3Definition, type Options } from 'swagger-jsdoc'; import { getRoutesAndCode } from './helpers/getRoutesAndCode'; +import { codeToAst } from './helpers/codeToAST'; +import traverse from '@babel/traverse'; +import * as doctrine from 'doctrine' export type SwaggerOptions = Options & { apiFolder?: string; @@ -57,7 +60,24 @@ export function createSwaggerSpec({ // For each of the api folders, parse as AST and get the route and possible return types const routesPathToCode = getRoutesAndCode(apiFolder); - console.log(routesPathToCode); + + Object.entries(routesPathToCode).map(([route, code]) => { + const ast = codeToAst(code); + traverse(ast, { + enter(path) { + if (path.node.leadingComments) { + for (const comment of path.node.leadingComments) { + if (comment.type === 'CommentBlock') { + const parsedComment = doctrine.parse(comment.value, { + unwrap: true, + }); + console.log('Parsed Comment:', parsedComment); + } + } + } + }, + }); + }); // Convert that data and append it to the swagger options // Conditions: basePath is specified. Server array is not defined. diff --git a/test/test-api-folder/app/api/route.ts b/test/test-api-folder/app/api/route.ts index b61e4bd4..6f66bdc8 100644 --- a/test/test-api-folder/app/api/route.ts +++ b/test/test-api-folder/app/api/route.ts @@ -1,13 +1,8 @@ import { NextResponse } from 'next/server'; /** - * @swagger - * /api/hello: - * get: - * description: Returns the hello world - * responses: - * 200: - * description: hello world + * @route + * description: This is a get request */ export async function GET() { return NextResponse.json({ data: 'sheesh' }); From 7efdf379dd78dfc2e73ad8fa792f41a4ec43498c Mon Sep 17 00:00:00 2001 From: Philip Andrew Wee De Wang Date: Sun, 1 Oct 2023 20:06:25 +0800 Subject: [PATCH 6/6] A very crude proof of concept done --- package.json | 4 +++- pnpm-lock.yaml | 15 +++++++++++++++ src/swagger.ts | 42 +++++++++++++++++++++++++++++++++++------- 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 0c7363b7..846ac64e 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,9 @@ "doctrine": "^3.0.0", "glob": "^10.3.10", "isarray": "2.0.5", - "swagger-jsdoc": "6.2.8" + "swagger-jsdoc": "6.2.8", + "yaml": "^2.3.2", + "zod": "^3.22.2" }, "devDependencies": { "@ianvs/prettier-plugin-sort-imports": "4.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 652c7591..99449cd5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,12 @@ dependencies: swagger-jsdoc: specifier: 6.2.8 version: 6.2.8(openapi-types@12.1.3) + yaml: + specifier: ^2.3.2 + version: 2.3.2 + zod: + specifier: ^3.22.2 + version: 3.22.2 devDependencies: '@ianvs/prettier-plugin-sort-imports': @@ -4686,6 +4692,11 @@ packages: engines: {node: '>= 14'} dev: true + /yaml@2.3.2: + resolution: {integrity: sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==} + engines: {node: '>= 14'} + dev: false + /yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -4759,3 +4770,7 @@ packages: /zod@3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} dev: true + + /zod@3.22.2: + resolution: {integrity: sha512-wvWkphh5WQsJbVk1tbx1l1Ly4yg+XecD+Mq280uBGt9wa5BKSWf4Mhp6GmrkPixhMxmabYY7RbzlwVP32pbGCg==} + dev: false diff --git a/src/swagger.ts b/src/swagger.ts index 478014c3..f835cc93 100644 --- a/src/swagger.ts +++ b/src/swagger.ts @@ -1,10 +1,16 @@ +import traverse from '@babel/traverse'; +import * as doctrine from 'doctrine'; import { type NextApiRequest, type NextApiResponse } from 'next'; import { join } from 'path'; -import swaggerJsdoc, { type OAS3Definition, type Options } from 'swagger-jsdoc'; -import { getRoutesAndCode } from './helpers/getRoutesAndCode'; +import swaggerJsdoc, { + type Paths, + type OAS3Definition, + type Options, +} from 'swagger-jsdoc'; +import { parse } from 'yaml'; +import { z } from 'zod'; import { codeToAst } from './helpers/codeToAST'; -import traverse from '@babel/traverse'; -import * as doctrine from 'doctrine' +import { getRoutesAndCode } from './helpers/getRoutesAndCode'; export type SwaggerOptions = Options & { apiFolder?: string; @@ -61,7 +67,9 @@ export function createSwaggerSpec({ // For each of the api folders, parse as AST and get the route and possible return types const routesPathToCode = getRoutesAndCode(apiFolder); - Object.entries(routesPathToCode).map(([route, code]) => { + const paths: Paths = {}; + + Object.entries(routesPathToCode).forEach(([route, code]) => { const ast = codeToAst(code); traverse(ast, { enter(path) { @@ -71,7 +79,23 @@ export function createSwaggerSpec({ const parsedComment = doctrine.parse(comment.value, { unwrap: true, }); - console.log('Parsed Comment:', parsedComment); + // Get the route tag + const routeTag = parsedComment.tags.find( + (tag) => tag.title === 'route' + ); + if (!routeTag) return; + const routeDescription = routeTag.description; + if (!routeDescription) return; + const parsedYaml = parse(routeDescription); + const schema = z.object({ + description: z.string(), + }); + const { description } = schema.parse(parsedYaml); + paths[route] = { + get: { + description, + }, + }; } } } @@ -83,6 +107,10 @@ export function createSwaggerSpec({ // Conditions: basePath is specified. Server array is not defined. const definition = { ...swaggerOptions.definition, + paths: { + ...paths, + ...swaggerOptions.definition.paths, + }, ...(process.env.__NEXT_ROUTER_BASEPATH && !swaggerOptions.definition.servers && { servers: [ @@ -94,7 +122,7 @@ export function createSwaggerSpec({ }), }; - const options: Options = { + const options: SwaggerOptions = { apis, // Files containing annotations as above ...swaggerOptions, definition,