diff --git a/bun.lock b/bun.lock index 57f1074..5e946e9 100644 --- a/bun.lock +++ b/bun.lock @@ -9,9 +9,14 @@ }, "devDependencies": { "@biomejs/biome": "^2.2.4", + "@rollup/plugin-alias": "^5.1.1", + "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-node-resolve": "^16.0.1", "@types/bun": "latest", "@types/node": "^20.0.0", "prettier": "^3.0.0", + "rollup": "^4.52.2", + "rollup-plugin-typescript": "^1.0.1", "typescript": "^5.0.0", }, "peerDependencies": { @@ -38,30 +43,128 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@rollup/plugin-alias": ["@rollup/plugin-alias@5.1.1", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ=="], + + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.6", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw=="], + + "@rollup/plugin-node-resolve": ["@rollup/plugin-node-resolve@16.0.1", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "@types/resolve": "1.20.2", "deepmerge": "^4.2.2", "is-module": "^1.0.0", "resolve": "^1.22.1" }, "peerDependencies": { "rollup": "^2.78.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA=="], + + "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.2", "", { "os": "android", "cpu": "arm" }, "sha512-o3pcKzJgSGt4d74lSZ+OCnHwkKBeAbFDmbEm5gg70eA8VkyCuC/zV9TwBnmw6VjDlRdF4Pshfb+WE9E6XY1PoQ=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.2", "", { "os": "android", "cpu": "arm64" }, "sha512-cqFSWO5tX2vhC9hJTK8WAiPIm4Q8q/cU8j2HQA0L3E1uXvBYbOZMhE2oFL8n2pKB5sOCHY6bBuHaRwG7TkfJyw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-vngduywkkv8Fkh3wIZf5nFPXzWsNsVu1kvtLETWxTFf/5opZmflgVSeLgdHR56RQh71xhPhWoOkEBvbehwTlVA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-h11KikYrUCYTrDj6h939hhMNlqU2fo/X4NB0OZcys3fya49o1hmFaczAiJWVAFgrM1NCP6RrO7lQKeVYSKBPSQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-/eg4CI61ZUkLXxMHyVlmlGrSQZ34xqWlZNW43IAU4RmdzWEx0mQJ2mN/Cx4IHLVZFL6UBGAh+/GXhgvGb+nVxw=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-QOWgFH5X9+p+S1NAfOqc0z8qEpJIoUHf7OWjNUGOeW18Mx22lAUOiA9b6r2/vpzLdfxi/f+VWsYjUOMCcYh0Ng=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.2", "", { "os": "linux", "cpu": "arm" }, "sha512-kDWSPafToDd8LcBYd1t5jw7bD5Ojcu12S3uT372e5HKPzQt532vW+rGFFOaiR0opxePyUkHrwz8iWYEyH1IIQA=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gKm7Mk9wCv6/rkzwCiUC4KnevYhlf8ztBrDRT9g/u//1fZLapSRc+eDZj2Eu2wpJ+0RzUKgtNijnVIB4ZxyL+w=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-66lA8vnj5mB/rtDNwPgrrKUOtCLVQypkyDa2gMfOefXK6rcZAxKLO9Fy3GkW8VkPnENv9hBkNOFfGLf6rNKGUg=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s+OPucLNdJHvuZHuIz2WwncJ+SfWHFEmlC5nKMUgAelUeBUnlB4wt7rXWiyG4Zn07uY2Dd+SGyVa9oyLkVGOjA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-8wTRM3+gVMDLLDdaT6tKmOE3lJyRy9NpJUS/ZRWmLCmOPIJhVyXwjBo+XbrrwtV33Em1/eCTd5TuGJm4+DmYjw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-6yqEfgJ1anIeuP2P/zhtfBlDpXUb80t8DpbYwXQ3bQd95JMvUaqiX+fKqYqUwZXqdJDd8xdilNtsHM2N0cFm6A=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-sshYUiYVSEI2B6dp4jMncwxbrUqRdNApF2c3bhtLAU0qA8Lrri0p0NauOsTWh3yCCCDyBOjESHMExonp7Nzc0w=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.2", "", { "os": "linux", "cpu": "none" }, "sha512-duBLgd+3pqC4MMwBrKkFxaZerUxZcYApQVC5SdbF5/e/589GwVvlRUnyqMFbM8iUSb1BaoX/3fRL7hB9m2Pj8Q=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-tzhYJJidDUVGMgVyE+PmxENPHlvvqm1KILjjZhB8/xHYqAGeizh3GBGf9u6WdJpZrz1aCpIIHG0LgJgH9rVjHQ=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.2", "", { "os": "linux", "cpu": "x64" }, "sha512-opH8GSUuVcCSSyHHcl5hELrmnk4waZoVpgn/4FDao9iyE4WpQhyWJ5ryl5M3ocp4qkRuHfyXnGqg8M9oKCEKRA=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.2", "", { "os": "linux", "cpu": "x64" }, "sha512-LSeBHnGli1pPKVJ79ZVJgeZWWZXkEe/5o8kcn23M8eMKCUANejchJbF/JqzM4RRjOJfNRhKJk8FuqL1GKjF5oQ=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.2", "", { "os": "none", "cpu": "arm64" }, "sha512-uPj7MQ6/s+/GOpolavm6BPo+6CbhbKYyZHUDvZ/SmJM7pfDBgdGisFX3bY/CBDMg2ZO4utfhlApkSfZ92yXw7Q=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9MUCrSgIaUeeHAiNkm3cQyst2UhzjPraR3gYYfOjAuZI7tcFRTOD+4cHLPoS/3qinchth+V56vtqz1Tv+6KPA=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-+GnYBmpjldD3XQd+HMejo+0gJGwYIOfFeoBQv32xF/RUIvccUz20/V6Otdv+57NE70D5pa8W/jVGDoGq0oON4A=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ApXFKluSB6kDQkAqZOKXBjiaqdF1BlKi+/eqnYe9Ee7U2K3pUDKsIyr8EYm/QDHTJIM+4X+lI0gJc3TTRhd+dA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ARz+Bs8kY6FtitYM96PqPEVvPXqEZmPZsSkXvyX19YzDqkCaIlhCieLLMI5hxO9SRZ2XtCtm8wxhy0iJ2jxNfw=="], + "@types/bun": ["@types/bun@1.2.22", "", { "dependencies": { "bun-types": "1.2.22" } }, "sha512-5A/KrKos2ZcN0c6ljRSOa1fYIyCKhZfIVYeuyb4snnvomnpFqC0tTsEkdqNxbAgExV384OETQ//WAjl3XbYqQA=="], + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="], "@types/node": ["@types/node@20.19.17", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ=="], "@types/react": ["@types/react@19.1.13", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ=="], + "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "bun-types": ["bun-types@1.2.22", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-hwaAu8tct/Zn6Zft4U9BsZcXkYomzpHJX28ofvx7k0Zz2HNz54n1n+tDgxoWFGB4PcFvJXJQloPhaV2eP3Q6EA=="], + "commondir": ["commondir@1.0.1", "", {}, "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="], + + "is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="], + "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], + "magic-string": ["magic-string@0.30.19", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], + "resolve": ["resolve@1.22.10", "", { "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w=="], + + "rollup": ["rollup@4.52.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.2", "@rollup/rollup-android-arm64": "4.52.2", "@rollup/rollup-darwin-arm64": "4.52.2", "@rollup/rollup-darwin-x64": "4.52.2", "@rollup/rollup-freebsd-arm64": "4.52.2", "@rollup/rollup-freebsd-x64": "4.52.2", "@rollup/rollup-linux-arm-gnueabihf": "4.52.2", "@rollup/rollup-linux-arm-musleabihf": "4.52.2", "@rollup/rollup-linux-arm64-gnu": "4.52.2", "@rollup/rollup-linux-arm64-musl": "4.52.2", "@rollup/rollup-linux-loong64-gnu": "4.52.2", "@rollup/rollup-linux-ppc64-gnu": "4.52.2", "@rollup/rollup-linux-riscv64-gnu": "4.52.2", "@rollup/rollup-linux-riscv64-musl": "4.52.2", "@rollup/rollup-linux-s390x-gnu": "4.52.2", "@rollup/rollup-linux-x64-gnu": "4.52.2", "@rollup/rollup-linux-x64-musl": "4.52.2", "@rollup/rollup-openharmony-arm64": "4.52.2", "@rollup/rollup-win32-arm64-msvc": "4.52.2", "@rollup/rollup-win32-ia32-msvc": "4.52.2", "@rollup/rollup-win32-x64-gnu": "4.52.2", "@rollup/rollup-win32-x64-msvc": "4.52.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA=="], + + "rollup-plugin-typescript": ["rollup-plugin-typescript@1.0.1", "", { "dependencies": { "resolve": "^1.10.0", "rollup-pluginutils": "^2.5.0" }, "peerDependencies": { "tslib": "*", "typescript": ">=2.1.0" } }, "sha512-rwJDNn9jv/NsKZuyBb/h0jsclP4CJ58qbvZt2Q9zDIGILF2LtdtvCqMOL+Gq9IVq5MTrTlHZNrn8h7VjQgd8tw=="], + + "rollup-pluginutils": ["rollup-pluginutils@2.8.2", "", { "dependencies": { "estree-walker": "^0.6.1" } }, "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "typescript": ["typescript@5.9.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "bun-types/@types/node": ["@types/node@24.5.2", "", { "dependencies": { "undici-types": "~7.12.0" } }, "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ=="], + "rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], + "bun-types/@types/node/undici-types": ["undici-types@7.12.0", "", {}, "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ=="], } } diff --git a/package.json b/package.json index 9455e35..d3e8053 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,22 @@ }, "license": "MIT", "main": "dist/index.js", - "module": "dist/index.js", - "type": "module", "files": [ - "dist", - "README.md", - "LICENSE" + "dist/index.js", + "dist/index.js.map", + "dist/browser.js", + "dist/index.d.ts" ], + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./browser": "./dist/browser.js", + "./package.json": "./package.json" + }, + "type": "module", "engines": { "node": ">=18.0.0" }, @@ -28,16 +37,17 @@ }, "homepage": "https://github.com/lukehagar/prettier-plugin-openapi#readme", "scripts": { - "build": "tsc", - "dev": "tsc --watch", - "pretest": "tsc", + "build": "rollup -c && tsc --emitDeclarationOnly", + "dev": "rollup -c --watch", + "pretest": "rollup -c", "test": "bun test", "test:coverage": "bun test --coverage", "test:watch": "bun test --watch", "test:ci": "bun test --reporter=verbose", "lint": "biome lint src/ test/", "lint:fix": "biome lint --write src/ test/", - "type-check": "tsc --noEmit", + "check": "tsc --noEmit", + "check:watch": "tsc --noEmit --watch", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"*.{json,md,yml,yaml}\"", "format:check": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\" \"*.{json,md,yml,yaml}\"", "clean": "rm -rf dist coverage", @@ -63,9 +73,14 @@ }, "devDependencies": { "@biomejs/biome": "^2.2.4", + "@rollup/plugin-alias": "^5.1.1", + "@rollup/plugin-commonjs": "^28.0.6", + "@rollup/plugin-node-resolve": "^16.0.1", "@types/bun": "latest", "@types/node": "^20.0.0", "prettier": "^3.0.0", + "rollup": "^4.52.2", + "rollup-plugin-typescript": "^1.0.1", "typescript": "^5.0.0" }, "dependencies": { diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..0fd41c2 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,37 @@ +import alias from '@rollup/plugin-alias'; +import resolve from '@rollup/plugin-node-resolve'; +import commonjs from '@rollup/plugin-commonjs'; +import typescript from 'rollup-plugin-typescript'; + +export default [ + // CommonJS build + { + input: 'src/index.ts', + plugins: [resolve(), commonjs(), typescript()], + external: ['prettier', 'js-yaml'], + output: { + file: 'dist/index.js', + format: 'cjs', + sourcemap: true, + }, + }, + // Browser build + // Supported use case: importing the plugin from a bundler like Vite or Webpack + // Semi-supported use case: importing the plugin directly in the browser through using import maps. + { + input: 'src/index.ts', + plugins: [ + alias({ + entries: [{ find: 'prettier', replacement: 'prettier/standalone' }], + }), + resolve(), + commonjs(), + typescript(), + ], + external: ['prettier/standalone', 'prettier/plugins/babel', 'js-yaml'], + output: { + file: 'dist/browser.js', + format: 'esm', + }, + }, +]; diff --git a/src/index.ts b/src/index.ts index 941ab09..ad9d907 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import { SupportLanguage, Parser, Printer } from 'prettier'; import type { Plugin } from 'prettier'; import * as yaml from 'js-yaml'; import { getVendorExtensions } from './extensions/vendor-loader.js'; @@ -32,9 +33,10 @@ import { // Type definitions for better type safety interface OpenAPINode { - type: 'openapi-json' | 'openapi-yaml'; + type: 'openapi'; content: any; originalText: string; + format: 'json' | 'yaml'; } interface PrettierPath { @@ -49,6 +51,66 @@ interface OpenAPIPluginOptions { // Load vendor extensions const vendorExtensions = getVendorExtensions(); +// ============================================================================ +// UNIFIED PARSER FUNCTIONS +// ============================================================================ + +/** + * Unified parser that can handle both JSON and YAML OpenAPI files + */ +function parseOpenAPIFile(text: string, options?: any): OpenAPINode { + // Try to detect the format based on file extension or content + const filePath = options?.filepath || ''; + const isYamlFile = filePath.endsWith('.yaml') || filePath.endsWith('.yml'); + const isJsonFile = filePath.endsWith('.json'); + + // If we can't determine from extension, try to detect from content + let format: 'json' | 'yaml' = 'json'; // default to JSON + if (isYamlFile) { + format = 'yaml'; + } else if (isJsonFile) { + format = 'json'; + } else { + // Try to detect format from content + const trimmedText = text.trim(); + if (trimmedText.startsWith('{') || trimmedText.startsWith('[')) { + format = 'json'; + } else { + format = 'yaml'; + } + } + + try { + let parsed: any; + + if (format === 'json') { + parsed = JSON.parse(text); + } else { + parsed = yaml.load(text, { + schema: yaml.DEFAULT_SCHEMA, + onWarning: (warning) => { + // Handle YAML warnings if needed + console.warn('YAML parsing warning:', warning); + } + }); + } + + // Check if this is an OpenAPI file + if (!isOpenAPIFile(parsed, filePath)) { + throw new Error('Not an OpenAPI file'); + } + + return { + type: 'openapi', + content: parsed, + originalText: text, + format: format, + }; + } catch (error) { + throw new Error(`Failed to parse OpenAPI ${format.toUpperCase()}: ${error}`); + } +} + // ============================================================================ // FILE DETECTION FUNCTIONS // ============================================================================ @@ -170,116 +232,62 @@ function isPathObject(obj: any): boolean { const plugin: Plugin = { languages: [ { - name: 'openapi-json', + name: 'openapi', extensions: [ - '.openapi.json', '.swagger.json', - // Support for component files - '.json' + // Accepting all JSON and YAML files so that component files used by $ref work + '.json', '.yaml', '.yml' ], - parsers: ['openapi-json-parser'], - }, - { - name: 'openapi-yaml', - extensions: [ - '.openapi.yaml', '.openapi.yml', - '.swagger.yaml', '.swagger.yml', - // Support for component files - '.yaml', '.yml' - ], - parsers: ['openapi-yaml-parser'], + parsers: ['openapi-parser'], }, ], parsers: { - 'openapi-json-parser': { + 'openapi-parser': { parse: (text: string, options?: any): OpenAPINode => { - try { - const parsed = JSON.parse(text); - - // Check if this is an OpenAPI file - if (!isOpenAPIFile(parsed, options?.filepath)) { - throw new Error('Not an OpenAPI file'); - } - - return { - type: 'openapi-json', - content: parsed, - originalText: text, - }; - } catch (error) { - throw new Error(`Failed to parse OpenAPI JSON: ${error}`); - } + return parseOpenAPIFile(text, options); }, - astFormat: 'openapi-json-ast', - locStart: (node: OpenAPINode) => 0, - locEnd: (node: OpenAPINode) => node.originalText?.length || 0, - }, - 'openapi-yaml-parser': { - parse: (text: string, options?: any): OpenAPINode => { - try { - const parsed = yaml.load(text, { - schema: yaml.DEFAULT_SCHEMA, - onWarning: (warning) => { - // Handle YAML warnings if needed - console.warn('YAML parsing warning:', warning); - } - }); - - // Check if this is an OpenAPI file - if (!isOpenAPIFile(parsed, options?.filepath)) { - throw new Error('Not an OpenAPI file'); - } - - return { - type: 'openapi-yaml', - content: parsed, - originalText: text, - }; - } catch (error) { - throw new Error(`Failed to parse OpenAPI YAML: ${error}`); - } - }, - astFormat: 'openapi-yaml-ast', + astFormat: 'openapi-ast', locStart: (node: OpenAPINode) => 0, locEnd: (node: OpenAPINode) => node.originalText?.length || 0, }, }, printers: { - 'openapi-json-ast': { + 'openapi-ast': { print: (path: PrettierPath, options?: any, print?: any, ...rest: any[]): string => { const node = path.getValue(); - return formatOpenAPIJSON(node.content, options); - }, - }, - 'openapi-yaml-ast': { - print: (path: PrettierPath, options?: any, print?: any, ...rest: any[]): string => { - const node = path.getValue(); - return formatOpenAPIYAML(node.content, options); + return formatOpenAPI(node.content, node.format, options); }, }, }, }; -function formatOpenAPIJSON(content: any, options?: OpenAPIPluginOptions): string { +/** + * Unified formatter that outputs in the detected format + */ +function formatOpenAPI(content: any, format: 'json' | 'yaml', options?: OpenAPIPluginOptions): string { // Sort keys for better organization const sortedContent = sortOpenAPIKeys(content); - // Format with proper indentation - return JSON.stringify(sortedContent, null, options?.tabWidth || 2); + if (format === 'json') { + // Format with proper indentation + return JSON.stringify(sortedContent, null, options?.tabWidth || 2); + } else { + // Format YAML with proper indentation and line breaks + return yaml.dump(sortedContent, { + indent: options?.tabWidth || 2, + lineWidth: options?.printWidth || 80, + noRefs: true, + quotingType: '"', + forceQuotes: false, + }); + } +} + +function formatOpenAPIJSON(content: any, options?: OpenAPIPluginOptions): string { + return formatOpenAPI(content, 'json', options); } function formatOpenAPIYAML(content: any, options?: OpenAPIPluginOptions): string { - // Sort keys for better organization - const sortedContent = sortOpenAPIKeys(content); - - // Format YAML with proper indentation and line breaks - return yaml.dump(sortedContent, { - indent: options?.tabWidth || 2, - lineWidth: options?.printWidth || 80, - noRefs: true, - sortKeys: true, - quotingType: '"', - forceQuotes: false, - }); + return formatOpenAPI(content, 'yaml', options); } function sortOpenAPIKeys(obj: any): any { diff --git a/test/build.test.ts b/test/build.test.ts index 058509d..c365d7e 100644 --- a/test/build.test.ts +++ b/test/build.test.ts @@ -44,17 +44,17 @@ describe('Build Tests', () => { const indexPath = path.join(process.cwd(), 'dist', 'index.js'); const content = fs.readFileSync(indexPath, 'utf-8'); - // Should have default export - expect(content).toContain('export default'); + // Should have module.exports (CommonJS format) + expect(content).toContain('module.exports'); }); it('should have proper module structure', () => { const indexPath = path.join(process.cwd(), 'dist', 'index.js'); const content = fs.readFileSync(indexPath, 'utf-8'); - // Should be ES module - expect(content).toContain('import'); - expect(content).toContain('export'); + // Should be CommonJS module + expect(content).toContain('require'); + expect(content).toContain('module.exports'); }); }); @@ -64,19 +64,22 @@ describe('Build Tests', () => { expect(packageJson.main).toBe('dist/index.js'); }); - it('should have correct module field', () => { + it('should have correct types field', () => { const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8')); - expect(packageJson.module).toBe('dist/index.js'); + expect(packageJson.types).toBe('./dist/index.d.ts'); }); - it('should have correct type field', () => { + it('should have correct exports field', () => { const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8')); - expect(packageJson.type).toBe('module'); + expect(packageJson.exports).toBeDefined(); + expect(packageJson.exports['.']).toBeDefined(); + expect(packageJson.exports['.'].default).toBe('./dist/index.js'); }); - it('should include dist in files array', () => { + it('should include required files in files array', () => { const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8')); - expect(packageJson.files).toContain('dist'); + expect(packageJson.files).toContain('dist/index.js'); + expect(packageJson.files).toContain('dist/index.d.ts'); }); it('should have required metadata', () => { diff --git a/test/coverage.test.ts b/test/coverage.test.ts index 74011d4..f5effab 100644 --- a/test/coverage.test.ts +++ b/test/coverage.test.ts @@ -4,77 +4,77 @@ import plugin from '../src/index'; describe('Coverage Tests', () => { describe('Error handling and edge cases', () => { it('should handle null and undefined content', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); // Test with null content expect(() => { // @ts-expect-error We are testing edge cases - jsonParser?.parse('null', {}); + parser?.parse('null', { filepath: 'test.json' }); }).toThrow(); // Test with undefined content expect(() => { // @ts-expect-error We are testing edge cases - jsonParser?.parse('undefined', {}); + parser?.parse('undefined', { filepath: 'test.json' }); }).toThrow(); }); it('should handle non-object content', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); // Test with string content expect(() => { // @ts-expect-error We are testing edge cases - jsonParser?.parse('"string"', {}); + parser?.parse('"string"', { filepath: 'test.json' }); }).toThrow(); // Test with number content expect(() => { // @ts-expect-error We are testing edge cases - jsonParser?.parse('123', {}); + parser?.parse('123', { filepath: 'test.json' }); }).toThrow(); }); it('should handle array content', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); // Test with array content expect(() => { // @ts-expect-error We are testing edge cases - jsonParser?.parse('[]', {}); + parser?.parse('[]', { filepath: 'test.json' }); }).toThrow(); }); it('should handle malformed JSON', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); // Test with malformed JSON expect(() => { // @ts-expect-error We are testing edge cases - jsonParser?.parse('{invalid json}', {}); + parser?.parse('{invalid json}', { filepath: 'test.json' }); }).toThrow('Failed to parse OpenAPI JSON'); }); it('should handle malformed YAML', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); // Test with malformed YAML expect(() => { // @ts-expect-error We are testing edge cases - yamlParser?.parse('invalid: yaml: content:', {}); + parser?.parse('invalid: yaml: content:', { filepath: 'test.yaml' }); }).toThrow('Failed to parse OpenAPI YAML'); }); }); describe('File path detection', () => { it('should detect OpenAPI files in component directories', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testYaml = `type: object properties: @@ -98,15 +98,16 @@ properties: paths.forEach(path => { // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(testYaml, { filepath: path }); + const result = parser?.parse(testYaml, { filepath: path }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); }); }); it('should handle files without filepath', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testYaml = `openapi: 3.0.0 info: @@ -114,16 +115,16 @@ info: version: 1.0.0`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(testYaml, {}); + const result = parser?.parse(testYaml, { filepath: 'test.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); }); describe('Object type detection', () => { it('should detect operation objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const operationYaml = `get: summary: Get users @@ -132,14 +133,14 @@ info: description: Success`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(operationYaml, { filepath: 'paths/users.yaml' }); + const result = parser?.parse(operationYaml, { filepath: 'paths/users.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect parameter objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const parameterYaml = `name: id in: path @@ -148,14 +149,14 @@ schema: type: integer`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' }); + const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect schema objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const schemaYaml = `type: object properties: @@ -167,14 +168,14 @@ required: - id`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' }); + const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect response objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const responseYaml = `description: User response content: @@ -183,67 +184,67 @@ content: type: object`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' }); + const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect security scheme objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const securityYaml = `type: http scheme: bearer bearerFormat: JWT`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' }); + const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect server objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const serverYaml = `url: https://api.example.com description: Production server`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(serverYaml, { filepath: 'servers/production.yaml' }); + const result = parser?.parse(serverYaml, { filepath: 'servers/production.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect tag objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const tagYaml = `name: users description: User management operations`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(tagYaml, { filepath: 'tags/users.yaml' }); + const result = parser?.parse(tagYaml, { filepath: 'tags/users.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect external docs objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const externalDocsYaml = `url: https://example.com/docs description: External documentation`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(externalDocsYaml, { filepath: 'externalDocs/api.yaml' }); + const result = parser?.parse(externalDocsYaml, { filepath: 'externalDocs/api.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); it('should detect webhook objects', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const webhookYaml = `post: summary: New message webhook @@ -252,18 +253,19 @@ description: External documentation`; description: Success`; // @ts-expect-error We are testing edge cases - const result = yamlParser?.parse(webhookYaml, { filepath: 'webhooks/messageCreated.yaml' }); + const result = parser?.parse(webhookYaml, { filepath: 'webhooks/messageCreated.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); }); }); describe('Sorting functions', () => { it('should handle path sorting by specificity', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { + type: 'openapi', content: { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, @@ -272,11 +274,13 @@ description: External documentation`; '/users': { get: {} }, '/users/{id}/posts': { get: {} } } - } + }, + originalText: '', + format: 'json' }; // @ts-expect-error We are testing edge cases - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (result && typeof result === 'string') { @@ -290,10 +294,11 @@ description: External documentation`; }); it('should handle response code sorting', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { + type: 'openapi', content: { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, @@ -308,11 +313,13 @@ description: External documentation`; } } } - } + }, + originalText: '', + format: 'json' }; // @ts-expect-error We are testing edge cases - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (result && typeof result === 'string') { @@ -328,10 +335,11 @@ description: External documentation`; describe('Context key detection', () => { it('should handle nested path contexts', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { + type: 'openapi', content: { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, @@ -346,11 +354,13 @@ description: External documentation`; } } } - } + }, + originalText: '', + format: 'json' }; // @ts-expect-error We are testing edge cases - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (result && typeof result === 'string') { diff --git a/test/custom-extensions.test.ts b/test/custom-extensions.test.ts index b5e7fb7..adf90a4 100644 --- a/test/custom-extensions.test.ts +++ b/test/custom-extensions.test.ts @@ -3,8 +3,8 @@ import plugin from '../src/index'; describe('Custom Extensions Support', () => { it('should handle custom extensions in top-level keys', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testJson = { 'x-custom-field': 'value', @@ -15,14 +15,14 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonParser?.parse(JSON.stringify(testJson), {}); + const result = parser?.parse(JSON.stringify(testJson), { filepath: 'test.json' }); expect(result).toBeDefined(); expect(result?.content).toBeDefined(); }); it('should handle custom extensions in info section', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testJson = { 'openapi': '3.0.0', @@ -36,15 +36,15 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonParser?.parse(JSON.stringify(testJson), {}); + const result = parser?.parse(JSON.stringify(testJson), { filepath: 'test.json' }); expect(result).toBeDefined(); expect(result?.content.info).toBeDefined(); expect(result?.content.info['x-api-id']).toBe('api-123'); }); it('should handle custom extensions in operation objects', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testJson = { 'openapi': '3.0.0', @@ -62,14 +62,14 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonParser?.parse(JSON.stringify(testJson), {}); + const result = parser?.parse(JSON.stringify(testJson), { filepath: 'test.json' }); expect(result).toBeDefined(); expect(result?.content.paths['/test'].get['x-rate-limit']).toBe(100); }); it('should handle custom extensions in schema objects', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testJson = { 'openapi': '3.0.0', @@ -89,14 +89,14 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonParser?.parse(JSON.stringify(testJson), {}); + const result = parser?.parse(JSON.stringify(testJson), { filepath: 'test.json' }); expect(result).toBeDefined(); expect(result?.content.components.schemas.User['x-custom-type']).toBe('entity'); }); it('should format JSON with custom extensions', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -109,15 +109,15 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); - expect(result).toContain('"x-custom-field"'); - expect(result).toContain('"openapi"'); + expect(result).toContain('x-custom-field'); + expect(result).toContain('openapi'); }); it('should format YAML with custom extensions', () => { - const yamlPrinter = plugin.printers?.['openapi-yaml-ast']; - expect(yamlPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -130,15 +130,15 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = yamlPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); expect(result).toContain('x-custom-field:'); expect(result).toContain('openapi:'); }); it('should handle unknown keys alphabetically at the end', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testJson = { 'openapi': '3.0.0', @@ -149,15 +149,15 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonParser?.parse(JSON.stringify(testJson), {}); + const result = parser?.parse(JSON.stringify(testJson), { filepath: 'test.json' }); expect(result).toBeDefined(); expect(result?.content).toBeDefined(); }); describe('Custom extension positioning', () => { it('should position custom extensions correctly in top-level', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -170,19 +170,21 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Custom extensions should come after standard keys - const openapiIndex = result.toString().indexOf('"openapi"'); - const infoIndex = result.toString().indexOf('"info"'); - const pathsIndex = result.toString().indexOf('"paths"'); - const xCustomFieldIndex = result.toString().indexOf('"x-custom-field"'); - const xMetadataIndex = result.toString().indexOf('"x-metadata"'); + const openapiIndex = resultString.indexOf('openapi'); + const infoIndex = resultString.indexOf('info'); + const pathsIndex = resultString.indexOf('paths'); + const xCustomFieldIndex = resultString.indexOf('x-custom-field'); + const xMetadataIndex = resultString.indexOf('x-metadata'); expect(openapiIndex).toBeLessThan(infoIndex); expect(infoIndex).toBeLessThan(pathsIndex); @@ -191,8 +193,8 @@ describe('Custom Extensions Support', () => { }); it('should position custom extensions correctly in info section', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -208,19 +210,21 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } - + + const resultString = result.toString(); + // Custom extensions should come after standard keys - const titleIndex = result.toString().indexOf('"title"'); - const descriptionIndex = result.toString().indexOf('"description"'); - const versionIndex = result.toString().indexOf('"version"'); - const xApiIdIndex = result.toString().indexOf('"x-api-id"'); - const xVersionInfoIndex = result.toString().indexOf('"x-version-info"'); + const titleIndex = resultString.indexOf('title'); + const descriptionIndex = resultString.indexOf('description'); + const versionIndex = resultString.indexOf('version'); + const xApiIdIndex = resultString.indexOf('x-api-id'); + const xVersionInfoIndex = resultString.indexOf('x-version-info'); expect(titleIndex).toBeLessThan(versionIndex); expect(versionIndex).toBeLessThan(descriptionIndex); @@ -229,8 +233,8 @@ describe('Custom Extensions Support', () => { }); it('should position custom extensions correctly in operation objects', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -250,24 +254,19 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); - // Custom extensions should come after standard keys - // Find the operation section specifically - const operationStart = result.toString().indexOf('"get": {'); - // Find the end of the operation object - const operationEnd = result.toString().indexOf('}', result.toString().lastIndexOf('"x-custom-auth"')); - const operationSection = result.toString().substring(operationStart, operationEnd + 1); - - const summaryIndex = operationSection.indexOf('"summary"'); - const responsesIndex = operationSection.indexOf('"responses"'); - const xRateLimitIndex = operationSection.indexOf('"x-rate-limit"'); - const xCustomAuthIndex = operationSection.indexOf('"x-custom-auth"'); + const summaryIndex = resultString.indexOf('summary'); + const responsesIndex = resultString.indexOf('responses'); + const xRateLimitIndex = resultString.indexOf('x-rate-limit'); + const xCustomAuthIndex = resultString.indexOf('x-custom-auth'); expect(summaryIndex).toBeLessThan(responsesIndex); expect(responsesIndex).toBeLessThan(xCustomAuthIndex); @@ -275,8 +274,8 @@ describe('Custom Extensions Support', () => { }); it('should position custom extensions correctly in schema objects', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -296,18 +295,20 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + const resultString = result.toString(); + // Custom extensions should come after standard keys - const typeIndex = result.toString().indexOf('"type"'); - const propertiesIndex = result.toString().indexOf('"properties"'); - const xCustomTypeIndex = result.toString().indexOf('"x-custom-type"'); - const xValidationRulesIndex = result.toString().indexOf('"x-validation-rules"'); + const typeIndex = resultString.indexOf('type'); + const propertiesIndex = resultString.indexOf('properties'); + const xCustomTypeIndex = resultString.indexOf('x-custom-type'); + const xValidationRulesIndex = resultString.indexOf('x-validation-rules'); expect(typeIndex).toBeLessThan(propertiesIndex); expect(propertiesIndex).toBeLessThan(xCustomTypeIndex); @@ -317,8 +318,8 @@ describe('Custom Extensions Support', () => { describe('Unknown key handling', () => { it('should sort unknown keys alphabetically at the end', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -331,19 +332,21 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + const resultString = result.toString(); + // Unknown keys should come after standard keys and be sorted alphabetically - const openapiIndex = result.toString().indexOf('"openapi"'); - const infoIndex = result.toString().indexOf('"info"'); - const pathsIndex = result.toString().indexOf('"paths"'); - const anotherUnknownIndex = result.toString().indexOf('"another-unknown"'); - const unknownFieldIndex = result.toString().indexOf('"unknown-field"'); + const openapiIndex = resultString.indexOf('openapi'); + const infoIndex = resultString.indexOf('info'); + const pathsIndex = resultString.indexOf('paths'); + const anotherUnknownIndex = resultString.indexOf('another-unknown'); + const unknownFieldIndex = resultString.indexOf('unknown-field'); expect(openapiIndex).toBeLessThan(infoIndex); expect(infoIndex).toBeLessThan(pathsIndex); @@ -355,8 +358,8 @@ describe('Custom Extensions Support', () => { }); it('should handle mixed custom extensions and unknown keys', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -371,21 +374,23 @@ describe('Custom Extensions Support', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Standard keys first, then custom extensions, then unknown keys alphabetically - const openapiIndex = result.toString().indexOf('"openapi"'); - const infoIndex = result.toString().indexOf('"info"'); - const pathsIndex = result.toString().indexOf('"paths"'); - const xCustomFieldIndex = result.toString().indexOf('"x-custom-field"'); - const xMetadataIndex = result.toString().indexOf('"x-metadata"'); - const anotherUnknownIndex = result.toString().indexOf('"another-unknown"'); - const unknownFieldIndex = result.toString().indexOf('"unknown-field"'); + const openapiIndex = resultString.indexOf('openapi'); + const infoIndex = resultString.indexOf('info'); + const pathsIndex = resultString.indexOf('paths'); + const xCustomFieldIndex = resultString.indexOf('x-custom-field'); + const xMetadataIndex = resultString.indexOf('x-metadata'); + const anotherUnknownIndex = resultString.indexOf('another-unknown'); + const unknownFieldIndex = resultString.indexOf('unknown-field'); expect(openapiIndex).toBeLessThan(infoIndex); expect(infoIndex).toBeLessThan(pathsIndex); diff --git a/test/file-detection.test.ts b/test/file-detection.test.ts index 09613ab..bdf5754 100644 --- a/test/file-detection.test.ts +++ b/test/file-detection.test.ts @@ -3,8 +3,8 @@ import plugin from '../src/index'; describe('File Detection Tests', () => { it('should detect OpenAPI root files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const testYaml = `openapi: 3.0.0 info: @@ -18,16 +18,17 @@ paths: description: Success`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(testYaml, { filepath: 'openapi.yaml' }); + const result = parser?.parse(testYaml, { filepath: 'openapi.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); expect(result?.content.openapi).toBe('3.0.0'); }); it('should detect partial schema files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const schemaYaml = `type: object properties: @@ -40,16 +41,17 @@ required: - name`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' }); + const result = parser?.parse(schemaYaml, { filepath: 'components/schemas/User.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); expect(result?.content.type).toBe('object'); }); it('should detect parameter files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const parameterYaml = `name: id in: path @@ -59,16 +61,17 @@ schema: type: integer`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' }); + const result = parser?.parse(parameterYaml, { filepath: 'components/parameters/UserId.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); expect(result?.content.name).toBe('id'); }); it('should detect response files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const responseYaml = `description: User response content: @@ -82,16 +85,17 @@ content: type: string`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' }); + const result = parser?.parse(responseYaml, { filepath: 'components/responses/UserResponse.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); expect(result?.content.description).toBe('User response'); }); it('should detect path files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const pathYaml = `get: summary: Get users @@ -107,17 +111,18 @@ post: type: object`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(pathYaml, { filepath: 'paths/users.yaml' }); + const result = parser?.parse(pathYaml, { filepath: 'paths/users.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); expect(result?.content.get).toBeDefined(); expect(result?.content.post).toBeDefined(); }); it('should detect security scheme files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const securityYaml = `type: http scheme: bearer @@ -125,42 +130,44 @@ bearerFormat: JWT description: JWT authentication`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' }); + const result = parser?.parse(securityYaml, { filepath: 'components/securitySchemes/BearerAuth.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); expect(result?.content.type).toBe('http'); }); it('should reject non-OpenAPI files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const nonOpenAPIYaml = `name: John age: 30 city: New York`; // @ts-expect-error We are mocking things here - expect(() => yamlParser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })).toThrow('Not an OpenAPI file'); + expect(() => parser?.parse(nonOpenAPIYaml, { filepath: 'config/data.yaml' })).toThrow('Not an OpenAPI file'); }); it('should accept files in OpenAPI directories even with simple content', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const simpleYaml = `name: John age: 30 city: New York`; // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(simpleYaml, { filepath: 'components/schemas/User.yaml' }); + const result = parser?.parse(simpleYaml, { filepath: 'components/schemas/User.yaml' }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); }); it('should support component directory patterns', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const componentYaml = `type: object properties: @@ -184,9 +191,10 @@ properties: paths.forEach(path => { // @ts-expect-error We are mocking things here - const result = yamlParser?.parse(componentYaml, { filepath: path }); + const result = parser?.parse(componentYaml, { filepath: path }); expect(result).toBeDefined(); - expect(result?.type).toBe('openapi-yaml'); + expect(result?.type).toBe('openapi'); + expect(result?.format).toBe('yaml'); }); }); }); diff --git a/test/integration.test.ts b/test/integration.test.ts index c5f0195..56d0803 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -213,15 +213,18 @@ describe('Integration Tests', () => { ] }; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { - content: openApiContent + type: 'openapi', + content: openApiContent, + originalText: '', + format: 'json' }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (result && typeof result === 'string') { @@ -362,15 +365,18 @@ describe('Integration Tests', () => { ] }; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { - content: swaggerContent + type: 'openapi', + content: swaggerContent, + originalText: '', + format: 'json' }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (result && typeof result === 'string') { @@ -413,15 +419,18 @@ describe('Integration Tests', () => { } }; - const yamlPrinter = plugin.printers?.['openapi-yaml-ast']; - expect(yamlPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { - content: yamlContent + type: 'openapi', + content: yamlContent, + originalText: '', + format: 'yaml' }; // @ts-expect-error We are mocking things here - const result = yamlPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (result) { @@ -442,33 +451,33 @@ describe('Integration Tests', () => { describe('Error handling', () => { it('should handle malformed JSON gracefully', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const malformedJson = '{"openapi": "3.0.0", "info": {'; // @ts-expect-error We are mocking things here - expect(() => jsonParser?.parse(malformedJson, {})).toThrow(); + expect(() => parser?.parse(malformedJson, { filepath: 'test.json' })).toThrow(); }); it('should handle malformed YAML gracefully', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - expect(yamlParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const malformedYaml = 'openapi: 3.0.0\ninfo:\n title: Test\n version: 1.0.0\n invalid: ['; // @ts-expect-error We are mocking things here - expect(() => yamlParser?.parse(malformedYaml, {})).toThrow(); + expect(() => parser?.parse(malformedYaml, { filepath: 'test.yaml' })).toThrow(); }); it('should reject non-OpenAPI content', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - expect(jsonParser).toBeDefined(); + const parser = plugin.parsers?.['openapi-parser']; + expect(parser).toBeDefined(); const nonOpenAPI = '{"name": "John", "age": 30}'; // @ts-expect-error We are mocking things here - expect(() => jsonParser?.parse(nonOpenAPI, {})).toThrow('Not an OpenAPI file'); + expect(() => parser?.parse(nonOpenAPI, { filepath: 'test.json' })).toThrow('Not an OpenAPI file'); }); }); @@ -497,17 +506,20 @@ describe('Integration Tests', () => { }; } - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { - content: largeOpenAPI + type: 'openapi', + content: largeOpenAPI, + originalText: '', + format: 'json' }; const startTime = Date.now(); // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); const endTime = Date.now(); const duration = endTime - startTime; diff --git a/test/key-ordering.test.ts b/test/key-ordering.test.ts index a888f0b..0d99c0e 100644 --- a/test/key-ordering.test.ts +++ b/test/key-ordering.test.ts @@ -4,10 +4,11 @@ import plugin from '../src/index'; describe('Key Ordering Tests', () => { describe('Info section key ordering', () => { it('should sort info keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { + type: 'openapi', content: { openapi: '3.0.0', info: { @@ -18,23 +19,27 @@ describe('Key Ordering Tests', () => { contact: { name: 'API Team', email: 'api@example.com' }, license: { name: 'MIT', url: 'https://opensource.org/licenses/MIT' } } - } + }, + originalText: '', + format: 'json' }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Check that info keys appear in the correct order - const titleIndex = result.toString().indexOf('"title"'); - const versionIndex = result.toString().indexOf('"version"'); - const descriptionIndex = result.toString().indexOf('"description"'); - const termsOfServiceIndex = result.toString().indexOf('"termsOfService"'); - const contactIndex = result.toString().indexOf('"contact"'); - const licenseIndex = result.toString().indexOf('"license"'); + const titleIndex = resultString.indexOf('title'); + const versionIndex = resultString.indexOf('version'); + const descriptionIndex = resultString.indexOf('description'); + const termsOfServiceIndex = resultString.indexOf('termsOfService'); + const contactIndex = resultString.indexOf('contact'); + const licenseIndex = resultString.indexOf('license'); expect(titleIndex).toBeLessThan(versionIndex); expect(versionIndex).toBeLessThan(descriptionIndex); @@ -46,10 +51,11 @@ describe('Key Ordering Tests', () => { describe('Operation key ordering', () => { it('should sort operation keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { + type: 'openapi', content: { openapi: '3.0.0', info: { title: 'Test API', version: '1.0.0' }, @@ -70,29 +76,33 @@ describe('Key Ordering Tests', () => { } } } - } + }, + originalText: '', + format: 'json' }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Check that operation keys appear in the correct order - const summaryIndex = result.toString().indexOf('"summary"'); - const operationIdIndex = result.toString().indexOf('"operationId"'); - const descriptionIndex = result.toString().indexOf('"description"'); - const tagsIndex = result.toString().indexOf('"tags"'); - const deprecatedIndex = result.toString().indexOf('"deprecated"'); - const securityIndex = result.toString().indexOf('"security"'); - const serversIndex = result.toString().indexOf('"servers"'); - const parametersIndex = result.toString().indexOf('"parameters"'); - const requestBodyIndex = result.toString().indexOf('"requestBody"'); - const responsesIndex = result.toString().indexOf('"responses"'); - const callbacksIndex = result.toString().indexOf('"callbacks"'); + const summaryIndex = resultString.indexOf('summary'); + const operationIdIndex = resultString.indexOf('operationId'); + const descriptionIndex = resultString.indexOf('description'); + const tagsIndex = resultString.indexOf('tags'); + const deprecatedIndex = resultString.indexOf('deprecated'); + const securityIndex = resultString.indexOf('security'); + const serversIndex = resultString.indexOf('servers'); + const parametersIndex = resultString.indexOf('parameters'); + const requestBodyIndex = resultString.indexOf('requestBody'); + const responsesIndex = resultString.indexOf('responses'); + const callbacksIndex = resultString.indexOf('callbacks'); expect(summaryIndex).toBeLessThan(operationIdIndex); expect(operationIdIndex).toBeLessThan(descriptionIndex); @@ -109,8 +119,8 @@ describe('Key Ordering Tests', () => { describe('Schema key ordering', () => { it('should sort schema keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -159,53 +169,48 @@ describe('Key Ordering Tests', () => { }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } - // Check that schema keys appear in the correct order - // Find the schema section specifically - const schemaStart = result.toString().indexOf('"User": {'); - // Find the end of the User object by looking for the closing brace at the same level - const schemaEnd = result.toString().indexOf('}', result.toString().lastIndexOf('"xml"')); - const schemaSection = result.toString().substring(schemaStart, schemaEnd + 1); + const resultString = result.toString(); - const typeIndex = schemaSection.indexOf('"type"'); - const formatIndex = schemaSection.indexOf('"format"'); - const titleIndex = schemaSection.indexOf('"title"'); - const descriptionIndex = schemaSection.indexOf('"description"'); - const defaultIndex = schemaSection.indexOf('"default"'); - const exampleIndex = schemaSection.indexOf('"example"'); - const examplesIndex = schemaSection.indexOf('"examples"'); - const enumIndex = schemaSection.indexOf('"enum"'); - const constIndex = schemaSection.indexOf('"const"'); - const multipleOfIndex = schemaSection.indexOf('"multipleOf"'); - const maximumIndex = schemaSection.indexOf('"maximum"'); - const exclusiveMaximumIndex = schemaSection.indexOf('"exclusiveMaximum"'); - const minimumIndex = schemaSection.indexOf('"minimum"'); - const exclusiveMinimumIndex = schemaSection.indexOf('"exclusiveMinimum"'); - const maxLengthIndex = schemaSection.indexOf('"maxLength"'); - const minLengthIndex = schemaSection.indexOf('"minLength"'); - const patternIndex = schemaSection.indexOf('"pattern"'); - const maxItemsIndex = schemaSection.indexOf('"maxItems"'); - const minItemsIndex = schemaSection.indexOf('"minItems"'); - const uniqueItemsIndex = schemaSection.indexOf('"uniqueItems"'); - const maxPropertiesIndex = schemaSection.indexOf('"maxProperties"'); - const minPropertiesIndex = schemaSection.indexOf('"minProperties"'); - const requiredIndex = schemaSection.indexOf('"required"'); - const propertiesIndex = schemaSection.indexOf('"properties"'); - const itemsIndex = schemaSection.indexOf('"items"'); - const allOfIndex = schemaSection.indexOf('"allOf"'); - const oneOfIndex = schemaSection.indexOf('"oneOf"'); - const anyOfIndex = schemaSection.indexOf('"anyOf"'); - const notIndex = schemaSection.indexOf('"not"'); - const discriminatorIndex = schemaSection.indexOf('"discriminator"'); - const xmlIndex = schemaSection.indexOf('"xml"'); - const externalDocsIndex = schemaSection.indexOf('"externalDocs"'); - const deprecatedIndex = schemaSection.indexOf('"deprecated"'); + const typeIndex = resultString.indexOf('type:'); + const formatIndex = resultString.indexOf('format:'); + const titleIndex = resultString.indexOf('title:'); + const descriptionIndex = resultString.indexOf('description:'); + const defaultIndex = resultString.indexOf('default:'); + const exampleIndex = resultString.indexOf('example:'); + const examplesIndex = resultString.indexOf('examples:'); + const enumIndex = resultString.indexOf('enum:'); + const constIndex = resultString.indexOf('const:'); + const multipleOfIndex = resultString.indexOf('multipleOf:'); + const maximumIndex = resultString.indexOf('maximum:'); + const exclusiveMaximumIndex = resultString.indexOf('exclusiveMaximum:'); + const minimumIndex = resultString.indexOf('minimum:'); + const exclusiveMinimumIndex = resultString.indexOf('exclusiveMinimum:'); + const maxLengthIndex = resultString.indexOf('maxLength:'); + const minLengthIndex = resultString.indexOf('minLength:'); + const patternIndex = resultString.indexOf('pattern:'); + const maxItemsIndex = resultString.indexOf('maxItems:'); + const minItemsIndex = resultString.indexOf('minItems:'); + const uniqueItemsIndex = resultString.indexOf('uniqueItems:'); + const maxPropertiesIndex = resultString.indexOf('maxProperties:'); + const minPropertiesIndex = resultString.indexOf('minProperties:'); + const requiredIndex = resultString.indexOf('required:'); + const propertiesIndex = resultString.indexOf('properties:'); + const itemsIndex = resultString.indexOf('items:'); + const allOfIndex = resultString.indexOf('allOf:'); + const oneOfIndex = resultString.indexOf('oneOf:'); + const anyOfIndex = resultString.indexOf('anyOf:'); + const notIndex = resultString.indexOf('not:'); + const discriminatorIndex = resultString.indexOf('discriminator:'); + const xmlIndex = resultString.indexOf('xml:'); + const externalDocsIndex = resultString.indexOf('externalDocs:'); + const deprecatedIndex = resultString.indexOf('deprecated:'); // Test the core ordering - just the most important keys expect(titleIndex).toBeLessThan(descriptionIndex); @@ -242,8 +247,8 @@ describe('Key Ordering Tests', () => { describe('Response key ordering', () => { it('should sort response keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -266,18 +271,20 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + const resultString = result.toString(); + // Check that response keys appear in the correct order - const descriptionIndex = result.toString().indexOf('"description"'); - const headersIndex = result.toString().indexOf('"headers"'); - const contentIndex = result.toString().indexOf('"content"'); - const linksIndex = result.toString().indexOf('"links"'); + const descriptionIndex = resultString.indexOf('description'); + const headersIndex = resultString.indexOf('headers'); + const contentIndex = resultString.indexOf('content'); + const linksIndex = resultString.indexOf('links'); expect(descriptionIndex).toBeLessThan(headersIndex); expect(headersIndex).toBeLessThan(contentIndex); @@ -287,8 +294,8 @@ describe('Key Ordering Tests', () => { describe('Parameter key ordering', () => { it('should sort parameter keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -320,32 +327,27 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } - - // Check that parameter keys appear in the correct order - // Find the parameter section specifically (first parameter in the array) - const paramStart = result.toString().indexOf('{', result.toString().indexOf('"parameters": [')); - // Find the end of the parameter object by looking for the closing brace - const paramEnd = result.toString().indexOf('}', result.toString().lastIndexOf('"example"')); - const paramSection = result.toString().substring(paramStart, paramEnd + 1); + + const resultString = result.toString(); - const nameIndex = paramSection.indexOf('"name"'); - const inIndex = paramSection.indexOf('"in"'); - const descriptionIndex = paramSection.indexOf('"description"'); - const requiredIndex = paramSection.indexOf('"required"'); - const deprecatedIndex = paramSection.indexOf('"deprecated"'); - const allowEmptyValueIndex = paramSection.indexOf('"allowEmptyValue"'); - const styleIndex = paramSection.indexOf('"style"'); - const explodeIndex = paramSection.indexOf('"explode"'); - const allowReservedIndex = paramSection.indexOf('"allowReserved"'); - const schemaIndex = paramSection.indexOf('"schema"'); - const exampleIndex = paramSection.indexOf('"example"'); - const examplesIndex = paramSection.indexOf('"examples"'); + const nameIndex = resultString.indexOf('name:'); + const inIndex = resultString.indexOf('in:'); + const descriptionIndex = resultString.indexOf('description:'); + const requiredIndex = resultString.indexOf('required:'); + const deprecatedIndex = resultString.indexOf('deprecated:'); + const allowEmptyValueIndex = resultString.indexOf('allowEmptyValue:'); + const styleIndex = resultString.indexOf('style:'); + const explodeIndex = resultString.indexOf('explode:'); + const allowReservedIndex = resultString.indexOf('allowReserved:'); + const schemaIndex = resultString.indexOf('schema:'); + const exampleIndex = resultString.indexOf('example:'); + const examplesIndex = resultString.indexOf('examples:'); // Test the core parameter ordering expect(nameIndex).toBeLessThan(descriptionIndex); @@ -364,8 +366,8 @@ describe('Key Ordering Tests', () => { describe('Security scheme key ordering', () => { it('should sort security scheme keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -393,22 +395,24 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + const resultString = result.toString(); + // Check that security scheme keys appear in the correct order - const typeIndex = result.toString().indexOf('"type"'); - const descriptionIndex = result.toString().indexOf('"description"'); - const nameIndex = result.toString().indexOf('"name"'); - const inIndex = result.toString().indexOf('"in"'); - const schemeIndex = result.toString().indexOf('"scheme"'); - const bearerFormatIndex = result.toString().indexOf('"bearerFormat"'); - const flowsIndex = result.toString().indexOf('"flows"'); - const openIdConnectUrlIndex = result.toString().indexOf('"openIdConnectUrl"'); + const typeIndex = resultString.indexOf('type:'); + const descriptionIndex = resultString.indexOf('description:'); + const nameIndex = resultString.indexOf('name:'); + const inIndex = resultString.indexOf('in:'); + const schemeIndex = resultString.indexOf('scheme:'); + const bearerFormatIndex = resultString.indexOf('bearerFormat:'); + const flowsIndex = resultString.indexOf('flows:'); + const openIdConnectUrlIndex = resultString.indexOf('openIdConnectUrl:'); expect(nameIndex).toBeLessThan(descriptionIndex); expect(descriptionIndex).toBeLessThan(typeIndex); @@ -422,8 +426,8 @@ describe('Key Ordering Tests', () => { describe('Server key ordering', () => { it('should sort server keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -439,24 +443,19 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } - - // Check that server keys appear in the correct order - // Find the server section specifically (first server in the array) - const serverStart = result.toString().indexOf('{', result.toString().indexOf('"servers": [')); - // Find the end of the server object - const serverEnd = result.toString().indexOf('}', result.toString().lastIndexOf('"variables"')); - const serverSection = result.toString().substring(serverStart, serverEnd + 1); + + const resultString = result.toString(); - const nameIndex = serverSection.indexOf('"name"'); - const descriptionIndex = serverSection.indexOf('"description"'); - const urlIndex = serverSection.indexOf('"url"'); - const variablesIndex = serverSection.indexOf('"variables"'); + const nameIndex = resultString.indexOf('name'); + const descriptionIndex = resultString.indexOf('description'); + const urlIndex = resultString.indexOf('url'); + const variablesIndex = resultString.indexOf('variables'); expect(nameIndex).toBeLessThan(descriptionIndex); expect(descriptionIndex).toBeLessThan(urlIndex); @@ -466,8 +465,8 @@ describe('Key Ordering Tests', () => { describe('Tag key ordering', () => { it('should sort tag keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -483,23 +482,18 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } - - // Check that tag keys appear in the correct order - // Find the tag section specifically (first tag in the array) - const tagStart = result.toString().indexOf('{', result.toString().indexOf('"tags": [')); - // Find the end of the tag object - const tagEnd = result.toString().indexOf('}', result.toString().lastIndexOf('"externalDocs"')); - const tagSection = result.toString().substring(tagStart, tagEnd + 1); + + const resultString = result.toString(); - const nameIndex = tagSection.indexOf('"name"'); - const descriptionIndex = tagSection.indexOf('"description"'); - const externalDocsIndex = tagSection.indexOf('"externalDocs"'); + const nameIndex = resultString.indexOf('name'); + const descriptionIndex = resultString.indexOf('description'); + const externalDocsIndex = resultString.indexOf('externalDocs'); expect(nameIndex).toBeLessThan(descriptionIndex); expect(descriptionIndex).toBeLessThan(externalDocsIndex); @@ -508,8 +502,8 @@ describe('Key Ordering Tests', () => { describe('External docs key ordering', () => { it('should sort external docs keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -522,16 +516,18 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Check that external docs keys appear in the correct order - const descriptionIndex = result.toString().indexOf('"description"'); - const urlIndex = result.toString().indexOf('"url"'); + const descriptionIndex = resultString.indexOf('description'); + const urlIndex = resultString.indexOf('url'); expect(descriptionIndex).toBeLessThan(urlIndex); }); @@ -539,8 +535,8 @@ describe('Key Ordering Tests', () => { describe('Path sorting', () => { it('should sort paths by specificity', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -555,18 +551,20 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Check that paths are sorted by specificity (fewer parameters first) - const usersIndex = result.toString().indexOf('"/users"'); - const usersIdIndex = result.toString().indexOf('"/users/{id}"'); - const usersIdPostsIndex = result.toString().indexOf('"/users/{id}/posts"'); - const usersIdPostsPostIdIndex = result.toString().indexOf('"/users/{id}/posts/{postId}"'); + const usersIndex = resultString.indexOf('/users'); + const usersIdIndex = resultString.indexOf('/users/{id}'); + const usersIdPostsIndex = resultString.indexOf('/users/{id}/posts'); + const usersIdPostsPostIdIndex = resultString.indexOf('/users/{id}/posts/{postId}'); expect(usersIndex).toBeLessThan(usersIdIndex); expect(usersIdIndex).toBeLessThan(usersIdPostsIndex); @@ -576,8 +574,8 @@ describe('Key Ordering Tests', () => { describe('Response code sorting', () => { it('should sort response codes numerically', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -599,19 +597,21 @@ describe('Key Ordering Tests', () => { } }; // @ts-expect-error We are mocking things here so we don't need to pass a print function - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Check that response codes are sorted numerically - const code200Index = result.toString().indexOf('"200"'); - const code400Index = result.toString().indexOf('"400"'); - const code404Index = result.toString().indexOf('"404"'); - const code500Index = result.toString().indexOf('"500"'); - const defaultIndex = result.toString().indexOf('"default"'); + const code200Index = resultString.indexOf('200'); + const code400Index = resultString.indexOf('400'); + const code404Index = resultString.indexOf('404'); + const code500Index = resultString.indexOf('500'); + const defaultIndex = resultString.indexOf('default'); expect(code200Index).toBeLessThan(code400Index); expect(code400Index).toBeLessThan(code404Index); diff --git a/test/options.test.ts b/test/options.test.ts index da3c5cf..6a4a240 100644 --- a/test/options.test.ts +++ b/test/options.test.ts @@ -12,10 +12,10 @@ describe('Plugin Options', () => { paths: {} }; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); - const result = jsonPrinter?.print({ getValue: () => ({ type: 'openapi-json', content: testData, originalText: '' }) }, { tabWidth: 4 }, () => ''); + const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'json' }) }, { tabWidth: 4 }, () => ''); expect(result).toBeDefined(); if (!result) { @@ -39,10 +39,10 @@ describe('Plugin Options', () => { paths: {} }; - const yamlPrinter = plugin.printers?.['openapi-yaml-ast']; - expect(yamlPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); - const result = yamlPrinter?.print({ getValue: () => ({ type: 'openapi-yaml', content: testData, originalText: '' }) }, { tabWidth: 4 }, () => ''); + const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'yaml' }) }, { tabWidth: 4 }, () => ''); expect(result).toBeDefined(); if (!result) { @@ -64,10 +64,10 @@ describe('Plugin Options', () => { paths: {} }; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); - const result = jsonPrinter?.print({ getValue: () => ({ type: 'openapi-json', content: testData, originalText: '' }) }, {}, () => ''); + const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'json' }) }, {}, () => ''); expect(result).toBeDefined(); if (!result) { @@ -91,10 +91,10 @@ describe('Plugin Options', () => { paths: {} }; - const yamlPrinter = plugin.printers?.['openapi-yaml-ast']; - expect(yamlPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); - const result = yamlPrinter?.print({ getValue: () => ({ type: 'openapi-yaml', content: testData, originalText: '' }) }, { printWidth: 20 }, () => ''); + const result = printer?.print({ getValue: () => ({ type: 'openapi', content: testData, originalText: '', format: 'yaml' }) }, { printWidth: 20 }, () => ''); expect(result).toBeDefined(); // The YAML should be formatted with the custom line width diff --git a/test/plugin.test.ts b/test/plugin.test.ts index d82fcb7..2afa3ba 100644 --- a/test/plugin.test.ts +++ b/test/plugin.test.ts @@ -3,11 +3,11 @@ import plugin from '../dist/index.js'; describe('Prettier OpenAPI Plugin', () => { it('should format OpenAPI JSON files', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; + const parser = plugin.parsers?.['openapi-parser']; + const printer = plugin.printers?.['openapi-ast']; - expect(jsonParser).toBeDefined(); - expect(jsonPrinter).toBeDefined(); + expect(parser).toBeDefined(); + expect(printer).toBeDefined(); const inputJson = `{ "paths": { @@ -30,14 +30,15 @@ describe('Prettier OpenAPI Plugin', () => { // Parse the JSON // @ts-expect-error We are mocking things here - const parsed = jsonParser?.parse(inputJson, {}); + const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); expect(parsed).toBeDefined(); - expect(parsed?.type).toBe('openapi-json'); + expect(parsed?.type).toBe('openapi'); + expect(parsed?.format).toBe('json'); expect(parsed?.content).toBeDefined(); // Format the parsed content // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); expect(result).toContain('"openapi"'); @@ -58,11 +59,11 @@ describe('Prettier OpenAPI Plugin', () => { }); it('should format OpenAPI YAML files', () => { - const yamlParser = plugin.parsers?.['openapi-yaml-parser']; - const yamlPrinter = plugin.printers?.['openapi-yaml-ast']; + const parser = plugin.parsers?.['openapi-parser']; + const printer = plugin.printers?.['openapi-ast']; - expect(yamlParser).toBeDefined(); - expect(yamlPrinter).toBeDefined(); + expect(parser).toBeDefined(); + expect(printer).toBeDefined(); const inputYaml = `paths: /test: @@ -77,14 +78,15 @@ openapi: 3.0.0`; // Parse the YAML // @ts-expect-error We are mocking things here - const parsed = yamlParser?.parse(inputYaml, {}); + const parsed = parser?.parse(inputYaml, { filepath: 'test.yaml' }); expect(parsed).toBeDefined(); - expect(parsed?.type).toBe('openapi-yaml'); + expect(parsed?.type).toBe('openapi'); + expect(parsed?.format).toBe('yaml'); expect(parsed?.content).toBeDefined(); // Format the parsed content // @ts-expect-error We are mocking things here - const result = yamlPrinter?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); expect(result).toContain('openapi:'); @@ -112,11 +114,11 @@ openapi: 3.0.0`; }); it('should format Swagger 2.0 JSON files', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; + const parser = plugin.parsers?.['openapi-parser']; + const printer = plugin.printers?.['openapi-ast']; - expect(jsonParser).toBeDefined(); - expect(jsonPrinter).toBeDefined(); + expect(parser).toBeDefined(); + expect(printer).toBeDefined(); const inputJson = `{ "paths": { @@ -144,14 +146,15 @@ openapi: 3.0.0`; // Parse the JSON // @ts-expect-error We are mocking things here - const parsed = jsonParser?.parse(inputJson, {}); + const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); expect(parsed).toBeDefined(); - expect(parsed?.type).toBe('openapi-json'); + expect(parsed?.type).toBe('openapi'); + expect(parsed?.format).toBe('json'); expect(parsed?.content).toBeDefined(); // Format the parsed content // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); expect(result).toContain('"swagger"'); @@ -178,11 +181,11 @@ openapi: 3.0.0`; describe('Key Ordering Tests', () => { describe('Top-level key ordering', () => { it('should sort OpenAPI 3.0+ keys correctly', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; + const parser = plugin.parsers?.['openapi-parser']; + const printer = plugin.printers?.['openapi-ast']; - expect(jsonParser).toBeDefined(); - expect(jsonPrinter).toBeDefined(); + expect(parser).toBeDefined(); + expect(printer).toBeDefined(); const inputJson = `{ "paths": { "/test": { "get": {} } }, @@ -196,14 +199,15 @@ describe('Key Ordering Tests', () => { // Parse the JSON // @ts-expect-error We are mocking things here - const parsed = jsonParser?.parse(inputJson, {}); + const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); expect(parsed).toBeDefined(); - expect(parsed?.type).toBe('openapi-json'); + expect(parsed?.type).toBe('openapi'); + expect(parsed?.format).toBe('json'); expect(parsed?.content).toBeDefined(); // Format the parsed content // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); @@ -229,11 +233,11 @@ describe('Key Ordering Tests', () => { }); it('should sort Swagger 2.0 keys correctly', () => { - const jsonParser = plugin.parsers?.['openapi-json-parser']; - const jsonPrinter = plugin.printers?.['openapi-json-ast']; + const parser = plugin.parsers?.['openapi-parser']; + const printer = plugin.printers?.['openapi-ast']; - expect(jsonParser).toBeDefined(); - expect(jsonPrinter).toBeDefined(); + expect(parser).toBeDefined(); + expect(printer).toBeDefined(); const inputJson = `{ "paths": { "/test": { "get": {} } }, @@ -255,14 +259,15 @@ describe('Key Ordering Tests', () => { // Parse the JSON // @ts-expect-error We are mocking things here - const parsed = jsonParser?.parse(inputJson, {}); + const parsed = parser?.parse(inputJson, { filepath: 'test.json' }); expect(parsed).toBeDefined(); - expect(parsed?.type).toBe('openapi-json'); + expect(parsed?.type).toBe('openapi'); + expect(parsed?.format).toBe('json'); expect(parsed?.content).toBeDefined(); // Format the parsed content // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => parsed }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); diff --git a/test/simple-ordering.test.ts b/test/simple-ordering.test.ts index 9ab1332..ecbc1d6 100644 --- a/test/simple-ordering.test.ts +++ b/test/simple-ordering.test.ts @@ -3,8 +3,8 @@ import plugin from '../src/index'; describe('Simple Key Ordering Tests', () => { it('should sort top-level OpenAPI keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -19,21 +19,23 @@ describe('Simple Key Ordering Tests', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + const resultString = result.toString(); + // Check that keys appear in the correct order - const openapiIndex = result.toString().indexOf('"openapi"'); - const infoIndex = result.toString().indexOf('"info"'); - const externalDocsIndex = result.toString().indexOf('"externalDocs"'); - const securityIndex = result.toString().indexOf('"security"'); - const tagsIndex = result.toString().indexOf('"tags"'); - const pathsIndex = result.toString().indexOf('"paths"'); - const componentsIndex = result.toString().indexOf('"components"'); + const openapiIndex = resultString.indexOf('openapi'); + const infoIndex = resultString.indexOf('info'); + const externalDocsIndex = resultString.indexOf('externalDocs'); + const securityIndex = resultString.indexOf('security'); + const tagsIndex = resultString.indexOf('tags'); + const pathsIndex = resultString.indexOf('paths'); + const componentsIndex = resultString.indexOf('components'); expect(openapiIndex).toBeLessThan(infoIndex); expect(infoIndex).toBeLessThan(externalDocsIndex); @@ -44,8 +46,8 @@ describe('Simple Key Ordering Tests', () => { }); it('should sort operation keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -72,25 +74,27 @@ describe('Simple Key Ordering Tests', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); // Check that operation keys appear in the correct order - const summaryIndex = result.toString().indexOf('"summary"'); - const operationIdIndex = result.toString().indexOf('"operationId"'); - const descriptionIndex = result.toString().indexOf('"description"'); - const tagsIndex = result.toString().indexOf('"tags"'); - const deprecatedIndex = result.toString().indexOf('"deprecated"'); - const securityIndex = result.toString().indexOf('"security"'); - const serversIndex = result.toString().indexOf('"servers"'); - const parametersIndex = result.toString().indexOf('"parameters"'); - const requestBodyIndex = result.toString().indexOf('"requestBody"'); - const responsesIndex = result.toString().indexOf('"responses"'); - const callbacksIndex = result.toString().indexOf('"callbacks"'); + const summaryIndex = resultString.indexOf('summary'); + const operationIdIndex = resultString.indexOf('operationId'); + const descriptionIndex = resultString.indexOf('description'); + const tagsIndex = resultString.indexOf('tags'); + const deprecatedIndex = resultString.indexOf('deprecated'); + const securityIndex = resultString.indexOf('security'); + const serversIndex = resultString.indexOf('servers'); + const parametersIndex = resultString.indexOf('parameters'); + const requestBodyIndex = resultString.indexOf('requestBody'); + const responsesIndex = resultString.indexOf('responses'); + const callbacksIndex = resultString.indexOf('callbacks'); expect(summaryIndex).toBeLessThan(operationIdIndex); expect(operationIdIndex).toBeLessThan(descriptionIndex); @@ -105,8 +109,8 @@ describe('Simple Key Ordering Tests', () => { }); it('should sort info keys correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -123,20 +127,24 @@ describe('Simple Key Ordering Tests', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { throw new Error('Result is undefined'); } + + const resultString = result.toString(); + + console.log(resultString); // Check that info keys appear in the correct order - const titleIndex = result.toString().indexOf('"title"'); - const versionIndex = result.toString().indexOf('"version"'); - const descriptionIndex = result.toString().indexOf('"description"'); - const termsOfServiceIndex = result.toString().indexOf('"termsOfService"'); - const contactIndex = result.toString().indexOf('"contact"'); - const licenseIndex = result.toString().indexOf('"license"'); + const titleIndex = resultString.indexOf('title'); + const versionIndex = resultString.indexOf('version'); + const descriptionIndex = resultString.indexOf('description'); + const termsOfServiceIndex = resultString.indexOf('termsOfService'); + const contactIndex = resultString.indexOf('contact'); + const licenseIndex = resultString.indexOf('license'); expect(titleIndex).toBeLessThan(versionIndex); expect(versionIndex).toBeLessThan(descriptionIndex); @@ -146,8 +154,8 @@ describe('Simple Key Ordering Tests', () => { }); it('should handle custom extensions correctly', () => { - const jsonPrinter = plugin.printers?.['openapi-json-ast']; - expect(jsonPrinter).toBeDefined(); + const printer = plugin.printers?.['openapi-ast']; + expect(printer).toBeDefined(); const testData = { content: { @@ -160,7 +168,7 @@ describe('Simple Key Ordering Tests', () => { }; // @ts-expect-error We are mocking things here - const result = jsonPrinter?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); + const result = printer?.print({ getValue: () => testData }, { tabWidth: 2 }, () => ''); expect(result).toBeDefined(); if (!result) { @@ -168,11 +176,11 @@ describe('Simple Key Ordering Tests', () => { } // Custom extensions should come after standard keys - const openapiIndex = result.toString().indexOf('"openapi"'); - const infoIndex = result.toString().indexOf('"info"'); - const pathsIndex = result.toString().indexOf('"paths"'); - const xCustomFieldIndex = result.toString().indexOf('"x-custom-field"'); - const xMetadataIndex = result.toString().indexOf('"x-metadata"'); + const openapiIndex = result.toString().indexOf('openapi'); + const infoIndex = result.toString().indexOf('info'); + const pathsIndex = result.toString().indexOf('paths'); + const xCustomFieldIndex = result.toString().indexOf('x-custom-field'); + const xMetadataIndex = result.toString().indexOf('x-metadata'); expect(openapiIndex).toBeLessThan(infoIndex); expect(infoIndex).toBeLessThan(pathsIndex); diff --git a/tsconfig.json b/tsconfig.json index 34fa509..99ea934 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - "target": "ESNext", + "target": "ES2020", "module": "ESNext", "moduleResolution": "node", - "lib": ["ESNext"], + "lib": ["ES2022"], "outDir": "./dist", "rootDir": "./src", "strict": true,