Update package.json and bun.lock to include Rollup plugins, modify build scripts for Rollup integration, and enhance TypeScript configuration for improved module handling. Introduce rollup.config.js for bundling configurations and refactor parser and printer references in tests to align with new structure.

This commit is contained in:
Luke Hagar
2025-09-26 19:05:00 +00:00
parent 34f3cd51bc
commit 5e97d04fb2
14 changed files with 776 additions and 562 deletions

103
bun.lock
View File

@@ -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=="],
}
}

View File

@@ -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": {

37
rollup.config.js Normal file
View File

@@ -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',
},
},
];

View File

@@ -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 {

View File

@@ -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', () => {

View File

@@ -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') {

View File

@@ -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);

View File

@@ -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');
});
});
});

View File

@@ -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;

View File

@@ -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);

View File

@@ -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

View File

@@ -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();

View File

@@ -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);

View File

@@ -1,9 +1,9 @@
{
"compilerOptions": {
"target": "ESNext",
"target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ESNext"],
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,