diff --git a/package-lock.json b/package-lock.json index 0fa3862e..0e86f93d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -194,30 +194,30 @@ } }, "node_modules/@ast-grep/napi": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.39.7.tgz", - "integrity": "sha512-fkCG3cSc2LiLgUXCrx83uo+dxmoRTGXtc7xMxhLsjHt5JRyJcXznevg92yjEQlCusVm98WtT/urh3hh2/4NZVQ==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi/-/napi-0.39.9.tgz", + "integrity": "sha512-qtLLQq1a3isK0iaq0Drl7Qt4PqeyjTrpFxNdA/20O/jYkGiA/oJA8DLMn1bzczsfjlUohe4dg39bpeAqG02uvA==", "dev": true, "license": "MIT", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@ast-grep/napi-darwin-arm64": "0.39.7", - "@ast-grep/napi-darwin-x64": "0.39.7", - "@ast-grep/napi-linux-arm64-gnu": "0.39.7", - "@ast-grep/napi-linux-arm64-musl": "0.39.7", - "@ast-grep/napi-linux-x64-gnu": "0.39.7", - "@ast-grep/napi-linux-x64-musl": "0.39.7", - "@ast-grep/napi-win32-arm64-msvc": "0.39.7", - "@ast-grep/napi-win32-ia32-msvc": "0.39.7", - "@ast-grep/napi-win32-x64-msvc": "0.39.7" + "@ast-grep/napi-darwin-arm64": "0.39.9", + "@ast-grep/napi-darwin-x64": "0.39.9", + "@ast-grep/napi-linux-arm64-gnu": "0.39.9", + "@ast-grep/napi-linux-arm64-musl": "0.39.9", + "@ast-grep/napi-linux-x64-gnu": "0.39.9", + "@ast-grep/napi-linux-x64-musl": "0.39.9", + "@ast-grep/napi-win32-arm64-msvc": "0.39.9", + "@ast-grep/napi-win32-ia32-msvc": "0.39.9", + "@ast-grep/napi-win32-x64-msvc": "0.39.9" } }, "node_modules/@ast-grep/napi-darwin-arm64": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-arm64/-/napi-darwin-arm64-0.39.7.tgz", - "integrity": "sha512-P02ZhbUOKq2wMwlswiWGfnsGDL1lzPSMCfqVnm9NuJoVtZ1IsEEncDcvQnJp997VQJ1MOlP3kzzCgZcpREBnfQ==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-arm64/-/napi-darwin-arm64-0.39.9.tgz", + "integrity": "sha512-0gdBC2oPBkIBsh89yUXxJeK37QYRbp1qNZVuRGizT666ljgCw+2NIxHeQGNjwWuI0+g3exrd8FyqD3gMixRx3w==", "cpu": [ "arm64" ], @@ -232,9 +232,9 @@ } }, "node_modules/@ast-grep/napi-darwin-x64": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-x64/-/napi-darwin-x64-0.39.7.tgz", - "integrity": "sha512-PXeMgv6RxVj06/8X9k2gljJf9rroCI9/MRmlEV9ktZkTtg9qjG6QKXiJ4AqDGem6i1Hm2H/2O1IMa6hAn8Uehw==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-darwin-x64/-/napi-darwin-x64-0.39.9.tgz", + "integrity": "sha512-7QuQNFwcVj71hDAMBErF+mVq2h92vUdHLKa/vq58HdRpix5G3DZqcTKoFjwhCNzc2wsNRAjV+BiIR01znQSxLA==", "cpu": [ "x64" ], @@ -249,9 +249,9 @@ } }, "node_modules/@ast-grep/napi-linux-arm64-gnu": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-gnu/-/napi-linux-arm64-gnu-0.39.7.tgz", - "integrity": "sha512-dTA10IoJPEQUVfOHrHUvpM0uEnhVatF90lY7OyRFbrgjqggiacjSmQUWxTOWmXNKt/jHZxLhlumXVd+W87VrOA==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-gnu/-/napi-linux-arm64-gnu-0.39.9.tgz", + "integrity": "sha512-qXTjhagRYkPyHNACEzGi5q26OMRdSvLRRqi799oZaSDf96Xj+8tcB5+tFlH0NpqaP84GsZoQ15SfDXeoP4qdmQ==", "cpu": [ "arm64" ], @@ -266,9 +266,9 @@ } }, "node_modules/@ast-grep/napi-linux-arm64-musl": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-musl/-/napi-linux-arm64-musl-0.39.7.tgz", - "integrity": "sha512-DAkLxVGZR5yrDCBNdPV+Eu0tKG3Zxm8Lhj1AQ2AOYFD6d/mbiTzufuyiXlqOJgmf53V5288j16JdxtcQJKJOtA==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-arm64-musl/-/napi-linux-arm64-musl-0.39.9.tgz", + "integrity": "sha512-U9KVohyburVGEWiiREFq+iTxdMdSbRyXL+yzCVvGPmLPW4Ca4zpX9Cret4jCJBp8dljSIK0C7txXAc7fM+6rag==", "cpu": [ "arm64" ], @@ -283,9 +283,9 @@ } }, "node_modules/@ast-grep/napi-linux-x64-gnu": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-gnu/-/napi-linux-x64-gnu-0.39.7.tgz", - "integrity": "sha512-x0KKHwSqwinXEkSGOpb74HQ7v6paJPIssZhaJCO3ZPWe0U1VITqeHbukV85wpVz7VJPu5HztkKLIE7jVCRMHQw==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-gnu/-/napi-linux-x64-gnu-0.39.9.tgz", + "integrity": "sha512-C5WwiNr/eE7lS0vl9bNdSXrsGIUAga2SaqV3G+ogVl9HeRU2jtGhv+3AgzGvYWj+PtnbtuZ2Nxkm10gEVPhv1A==", "cpu": [ "x64" ], @@ -300,9 +300,9 @@ } }, "node_modules/@ast-grep/napi-linux-x64-musl": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-musl/-/napi-linux-x64-musl-0.39.7.tgz", - "integrity": "sha512-SXVWii/AWzME4HESkIvpm8yCqUdiGvwTAzq2f54PiL+12oodL3uFoA2ccpB5Vvq49e5C2OOeW0bkr8jzRure6A==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-linux-x64-musl/-/napi-linux-x64-musl-0.39.9.tgz", + "integrity": "sha512-KutzZSMhHP3P43nQ+kNSbuiDaqkCcdqOcXLnq28MRvS+qwvW+01dJ8eWRlpMJC9vCJmaJAZfI7xkNSp74WkdxA==", "cpu": [ "x64" ], @@ -317,9 +317,9 @@ } }, "node_modules/@ast-grep/napi-win32-arm64-msvc": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-arm64-msvc/-/napi-win32-arm64-msvc-0.39.7.tgz", - "integrity": "sha512-4w3wCgGOZADXnoS6zV9z2Qw2u+CxNSgjR7/6E69wb2dJ5nJOJXc2dingB3l1CZ2f1XOq5ag+PEtBINwvu0r2vA==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-arm64-msvc/-/napi-win32-arm64-msvc-0.39.9.tgz", + "integrity": "sha512-S2kC6K9I7e70oMVn2FDICzVlz7SPZVWNCRKCBD+Dgy1OTn0byzmMLaGpjth3pqN93UCssEC03ooP3tuclS0JSA==", "cpu": [ "arm64" ], @@ -334,9 +334,9 @@ } }, "node_modules/@ast-grep/napi-win32-ia32-msvc": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-ia32-msvc/-/napi-win32-ia32-msvc-0.39.7.tgz", - "integrity": "sha512-+CVjlvYbF4vYdltffG7dAmYAv4WBZPeBrsZfuXBe7+nNdM6MXrinT1uMX8nWKJMV8KA264xojlneyDsxSt2Ntw==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-ia32-msvc/-/napi-win32-ia32-msvc-0.39.9.tgz", + "integrity": "sha512-n18Dqtc7O5q8ZM9gBNkvLd+ajdjLNraw4ZoIcHelI7TnTvh1m6zR3tUuHNxPhL+IJfdKx3ne7ATKlEeiSlMn4g==", "cpu": [ "ia32" ], @@ -351,9 +351,9 @@ } }, "node_modules/@ast-grep/napi-win32-x64-msvc": { - "version": "0.39.7", - "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.39.7.tgz", - "integrity": "sha512-ZjfdsjcAmtg9TyTcTalqw9W+HyQH+C0Mx4rL6UieIEc2Z4tWqP/hTwOl9CJ4JPmRYsW6yKMIxjJPj9RF0mv4iA==", + "version": "0.39.9", + "resolved": "https://registry.npmjs.org/@ast-grep/napi-win32-x64-msvc/-/napi-win32-x64-msvc-0.39.9.tgz", + "integrity": "sha512-YOF7mO+6nvyRtMC179u82Vr1qIGOmoE1yjwr4cmAf7DVNd+vs42y9tbQGYwLmVY8M3Hg1PviB8XX7FQKewuOGQ==", "cpu": [ "x64" ], @@ -1472,6 +1472,10 @@ "node": ">=22 || ^20.6.0 || ^18.19.0" } }, + "node_modules/@nodejs/axios-to-whatwg-fetch": { + "resolved": "recipes/axios-to-whatwg-fetch", + "link": true + }, "node_modules/@nodejs/buffer-atob-btoa": { "resolved": "recipes/buffer-atob-btoa", "link": true @@ -1787,28 +1791,28 @@ } }, "node_modules/@typescript/native-preview": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-jRyx4j67IHO5a512iE7LGT6wD5DgOmedG6nEQkMIRQGiwRnO3ntzk01PBpFXBoNwiBRfQk6PlFTcljkFQT5RbA==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview/-/native-preview-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-v1SNmHbuTYMEIAAJZ5OgKY5kMIgDnS/aVTsP9FdR9FgqyZqgUbA2eHOjjMQHVw/XBLS5ZA32kkGt7cH8RzMlOA==", "dev": true, "license": "Apache-2.0", "bin": { "tsgo": "bin/tsgo.js" }, "optionalDependencies": { - "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251104.1", - "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251104.1", - "@typescript/native-preview-linux-arm": "7.0.0-dev.20251104.1", - "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251104.1", - "@typescript/native-preview-linux-x64": "7.0.0-dev.20251104.1", - "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251104.1", - "@typescript/native-preview-win32-x64": "7.0.0-dev.20251104.1" + "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20251108.1", + "@typescript/native-preview-darwin-x64": "7.0.0-dev.20251108.1", + "@typescript/native-preview-linux-arm": "7.0.0-dev.20251108.1", + "@typescript/native-preview-linux-arm64": "7.0.0-dev.20251108.1", + "@typescript/native-preview-linux-x64": "7.0.0-dev.20251108.1", + "@typescript/native-preview-win32-arm64": "7.0.0-dev.20251108.1", + "@typescript/native-preview-win32-x64": "7.0.0-dev.20251108.1" } }, "node_modules/@typescript/native-preview-darwin-arm64": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-4I/Xy2saFsrzfRhQ5H6gV2SwICvkS0PV5T+wWD0aKQTsffEFPtTBhUOTaTuzG1FpMMcdO5WC5qSUdIvqcIWD+Q==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-arm64/-/native-preview-darwin-arm64-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-zdD59CWlvJum9hu7Rrb7xntGfkeTlki7Pql/s+Ls0sNSEGimjuJmU88ookUt2UMPLZ9S+RyGfjNbS7duhKuCIw==", "cpu": [ "arm64" ], @@ -1820,9 +1824,9 @@ ] }, "node_modules/@typescript/native-preview-darwin-x64": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-03ymJ3eg0z9soEmN0Mg8UZPqsG4TwAYPFQLvNT1D8+meGKgihsGgUb05bBYhnvaq0M3HcG7XUJac1molOTxUOA==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-darwin-x64/-/native-preview-darwin-x64-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-nxs1vOm6jkwZoA56d6E2pllhYbxK2xNtspG2Yx9kswdp0aqh2jcja1EjMilHv4tnvNixRQvRWhoOz/BgWrxTig==", "cpu": [ "x64" ], @@ -1834,9 +1838,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-y+qu91VCz3bjI0m0d6hDCtmk+q2ToFd/Z3tjX1r8OyKgLQeetpdxpUrEZ+Y/uLZTfKXDfcv599IQI2Oma3vEiQ==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm/-/native-preview-linux-arm-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-+1UuKod2SJZODnArwHViJEhZxgM1aHK5KCGlHJU61htq3jgNtRfouw4UEJRPTALCnGLun8ZpYuMsJUMeDFFIUQ==", "cpu": [ "arm" ], @@ -1848,9 +1852,9 @@ ] }, "node_modules/@typescript/native-preview-linux-arm64": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-UM1d8mqyGnkvP3Uvy1GDW+XBQM9Sgl7Up1vGUV0JempuXHPf9fhI8swSBBCyLVVTUAgzxp79q1EvM6ieTkm8VA==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-arm64/-/native-preview-linux-arm64-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-Er0P4Dt6fBM6MZidGBKGZs5PS2QHunRPNeIKCATJRcBMn8ymSBdi/IJK/wYVU62HW8CLb1Dt3FbueW5UptSAKA==", "cpu": [ "arm64" ], @@ -1862,9 +1866,9 @@ ] }, "node_modules/@typescript/native-preview-linux-x64": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-TJ2Xh+isYw49Yi/c5vffQROjUHf2UsiSX7VB+Vh3ywVhOC9mEnLGS9L3D5UZguAMiotz1BoqU66GqEge/lbI1A==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-linux-x64/-/native-preview-linux-x64-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-3ijz+Uo8unENgq22nlyThf1JqhLVOwN881gomoAykALmspXX13aQwnDtbscnQRr1iQqMIrEyyRMwxfVevQkl9w==", "cpu": [ "x64" ], @@ -1876,9 +1880,9 @@ ] }, "node_modules/@typescript/native-preview-win32-arm64": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-DonN+wVOTeIwE72idHOqC2eXJ8pkpNI4DWAwA1DmepFI6TXeWln8yKEyak8yjguPVvvCrJEKvx4q26CVXfWThA==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-arm64/-/native-preview-win32-arm64-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-Aeyrj7sdc6GBnLIw9o5dQPzEqrfsJodDYsi7RePm0QLvGVyxOeZVd9CcfdXqPKnD2goEJNOzFoTRyvvi7DjNUg==", "cpu": [ "arm64" ], @@ -1890,9 +1894,9 @@ ] }, "node_modules/@typescript/native-preview-win32-x64": { - "version": "7.0.0-dev.20251104.1", - "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20251104.1.tgz", - "integrity": "sha512-kMhr8pCKcNBEl5+6q71MkBxMCukJNWQAg4WKsDDfrO5ImhAanHGkNfVD6g0nAPbRP3MjuXo1o4KwPdxyfHXUmg==", + "version": "7.0.0-dev.20251108.1", + "resolved": "https://registry.npmjs.org/@typescript/native-preview-win32-x64/-/native-preview-win32-x64-7.0.0-dev.20251108.1.tgz", + "integrity": "sha512-rgfq7AZ7IFfE5EyDuHdpDEPUH3LEesyOVuIHE6YWOSqGOFnlzYbOf37VUYxQTIxFrR7IopgvM4pSDR3KeDcMjQ==", "cpu": [ "x64" ], @@ -2025,9 +2029,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.24", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.24.tgz", - "integrity": "sha512-uUhTRDPXamakPyghwrUcjaGvvBqGrWvBHReoiULMIpOJVM9IYzQh83Xk2Onx5HlGI2o10NNCzcs9TG/S3TkwrQ==", + "version": "2.8.25", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.25.tgz", + "integrity": "sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -2149,9 +2153,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001753", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz", - "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==", + "version": "1.0.30001754", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001754.tgz", + "integrity": "sha512-x6OeBXueoAceOmotzx3PO4Zpt4rzpeIFsSr6AAePTZxSkXiYDUmpypEl7e2+8NCd9bD7bXjqyef8CJYPC1jfxg==", "funding": [ { "type": "opencollective", @@ -2359,7 +2363,6 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", - "dev": true, "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -2470,9 +2473,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.244", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz", - "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==", + "version": "1.5.249", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.249.tgz", + "integrity": "sha512-5vcfL3BBe++qZ5kuFhD/p8WOM1N9m3nwvJPULJx+4xf2usSlZFJ0qoNYO2fOX4hi3ocuDcmDobtA+5SFr4OmBg==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -2635,9 +2638,9 @@ } }, "node_modules/flow-parser": { - "version": "0.289.0", - "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.289.0.tgz", - "integrity": "sha512-w4sVnH6ddNAIxokoz0mGyiIIdzvqncFhAYW+RmkPbPSSTYozG6yhqAixzaWeBCQf2qqXJTlHkoKPnf/BAj8Ofw==", + "version": "0.290.0", + "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.290.0.tgz", + "integrity": "sha512-9qXeNyrHPIoRK23kX7HNp275RYMy2y1AWb37y86ZTH/2UvfrofBis18aBunzfTIXkRpeD0F/w/uAKFhLUpboqQ==", "license": "MIT", "engines": { "node": ">=0.4.0" @@ -4256,6 +4259,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "recipes/axios-to-whatwg-fetch": { + "name": "@nodejs/axios-to-whatwg-fetch", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@nodejs/codemod-utils": "*", + "dedent": "^1.7.0" + }, + "devDependencies": { + "@codemod.com/jssg-types": "^1.0.9" + } + }, "recipes/buffer-atob-btoa": { "name": "@nodejs/buffer-atob-btoa", "version": "1.0.0", diff --git a/recipes/axios-to-whatwg-fetch/README.md b/recipes/axios-to-whatwg-fetch/README.md new file mode 100644 index 00000000..7a076bbd --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/README.md @@ -0,0 +1,104 @@ +# Axios to WHATWG Fetch Codemod + +## Description + +This codemod transforms code using Axios to leverage the WHATWG Fetch API, which is now natively available in Node.js. By replacing Axios with Fetch, you can reduce dependencies, mitigate risks, and improve performance. + +## Supported Transformations + +The codemod supports the following Axios methods and converts them to their Fetch equivalents: + +- `axios.request(config)` +- `axios.get(url[, config])` +- `axios.delete(url[, config])` +- `axios.head(url[, config])` +- `axios.options(url[, config])` +- `axios.post(url[, data[, config]])` +- `axios.put(url[, data[, config]])` +- `axios.patch(url[, data[, config]])` + +### Examples + +#### GET Request + +```diff + const base = 'https://dummyjson.com/todos'; + +- const all = await axios.get(base); ++ const all = await fetch(base).then(async (res) => Object.assign(res, { data: await res.json() })).catch(() => null); + console.log('\nGET /todos ->', all.status); + console.log(`Preview: ${all.data.todos.length} todos`); +``` + +#### POST Request + +```diff + const base = 'https://dummyjson.com/todos'; + +- const created = await axios.post( +- `${base}/add`, { +- todo: 'Use DummyJSON in the project', +- completed: false, +- userId: 5, +- }, { +- headers: { 'Content-Type': 'application/json' } +- } +- ); ++ const created = await fetch(`${base}/add`, { ++ method: 'POST', ++ headers: { 'Content-Type': 'application/json' }, ++ body: JSON.stringify({ ++ todo: 'Use DummyJSON in the project', ++ completed: false, ++ userId: 5, ++ }), ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); + console.log('\nPOST /todos/add ->', created.status); + console.log('Preview:', created.data?.id ? `created id ${created.data.id}` : JSON.stringify(created.data).slice(0,200)); +``` + +#### PUT Request + +```diff + const base = 'https://dummyjson.com/todos'; + +- const updatedPut = await axios.put( +- `${base}/1`, +- { completed: false }, +- { headers: { 'Content-Type': 'application/json' } } +- ); ++ const updatedPut = await fetch(`${base}/1`, { ++ method: 'PUT', ++ headers: { 'Content-Type': 'application/json' }, ++ body: JSON.stringify({ completed: false }), ++ }).then(async (res) => Object.assign(res, { data: await res.json() })); + console.log('\nPUT /todos/1 ->', updatedPut.status); + console.log('Preview:', updatedPut.data?.completed !== undefined ? `completed=${updatedPut.data.completed}` : JSON.stringify(updatedPut.data).slice(0,200)); +``` + +#### DELETE Request + +```diff + const base = 'https://dummyjson.com/todos'; + +- const deleted = await axios.delete(`${base}/1`); ++ const deleted = await fetch(`${base}/1`, { method: 'DELETE' }) ++ .then(async (res) => Object.assign(res, { data: await res.json() })); + console.log('\nDELETE /todos/1 ->', deleted.status); + console.log('Preview:', deleted.data ? JSON.stringify(deleted.data).slice(0,200) : typeof deleted.data); +``` + +## Unsupported APIs + +The following Axios methods are not supported by this codemod and will generate warnings: + +- `axios.postForm(url[, data[, config]])` +- `axios.putForm(url[, data[, config]])` +- `axios.patchForm(url[, data[, config]])` + +## References + +- [Fetch Spec](https://fetch.spec.whatwg.org) +- [Axios Documentation](https://axios-http.com) +- [Node.js Documentation](https://nodejs.org/docs/latest/api/globals.html#fetch) + diff --git a/recipes/axios-to-whatwg-fetch/codemod.yaml b/recipes/axios-to-whatwg-fetch/codemod.yaml new file mode 100644 index 00000000..afb48c9a --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/codemod.yaml @@ -0,0 +1,21 @@ +schema_version: "1.0" +name: "@nodejs/util-print-to-console-log" +version: 1.0.0 +description: Replace `axios` with `fetch` +author: Bruno Rodrigues +license: MIT +workflow: workflow.yaml +category: migration + +targets: + languages: + - javascript + - typescript + +keywords: + - transformation + - migration + +registry: + access: public + visibility: public diff --git a/recipes/axios-to-whatwg-fetch/package.json b/recipes/axios-to-whatwg-fetch/package.json new file mode 100644 index 00000000..b1d1b183 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/package.json @@ -0,0 +1,25 @@ +{ + "name": "@nodejs/axios-to-whatwg-fetch", + "version": "1.0.0", + "description": "Replace `axios` with `fetch`", + "type": "module", + "scripts": { + "test": "npx codemod jssg test -l typescript ./src/workflow.ts ./" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/nodejs/userland-migrations.git", + "directory": "recipes/axios-to-whatwg-fetch", + "bugs": "https://github.com/nodejs/userland-migrations/issues" + }, + "author": "Bruno Rodrigues", + "license": "MIT", + "homepage": "https://github.com/nodejs/userland-migrations/blob/main/recipes/axios-to-whatwg-fetch/README.md", + "devDependencies": { + "@codemod.com/jssg-types": "^1.0.9" + }, + "dependencies": { + "@nodejs/codemod-utils": "*", + "dedent": "^1.7.0" + } +} diff --git a/recipes/axios-to-whatwg-fetch/src/workflow.ts b/recipes/axios-to-whatwg-fetch/src/workflow.ts new file mode 100644 index 00000000..72aef6ea --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/src/workflow.ts @@ -0,0 +1,266 @@ +import dedent from 'dedent'; +import { EOL } from 'node:os'; +import { getNodeRequireCalls } from '@nodejs/codemod-utils/ast-grep/require-call'; +import { getNodeImportStatements } from '@nodejs/codemod-utils/ast-grep/import-statement'; +import { resolveBindingPath } from '@nodejs/codemod-utils/ast-grep/resolve-binding-path'; +import { removeBinding } from '@nodejs/codemod-utils/ast-grep/remove-binding'; +import { removeLines } from '@nodejs/codemod-utils/ast-grep/remove-lines'; +import type { + Edit, + Range, + Rule, + SgNode, + SgRoot, +} from '@codemod.com/jssg-types/main'; +import type Js from '@codemod.com/jssg-types/langs/javascript'; + +type BindingToReplace = { + rule: Rule; + node: SgNode; + binding: string; + replaceFn: (arg: SgNode[]) => string; +}; + +type CreateOptionsType = { + oldOptions?: SgNode; + method?: string; + body?: string; +}; + +const unsupportedMethods = ['postForm', 'putForm', 'patchForm']; + +const updates: { oldBind: string; replaceFn: BindingToReplace['replaceFn'] }[] = + [ + /*{ + It's should be migratable with codemod but not supported for now + oldBind: '$.request', + replaceFn: (arg) => `console.log(${arg})`, + },*/ + { + oldBind: '$.get', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[1], + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}${options ? `, ${options}` : ''}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + { + oldBind: '$.post', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[2], + method: 'POST', + body: args[1]?.text(), + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}${options ? `, ${options}` : ''}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + { + oldBind: '$.put', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[2], + method: 'PUT', + body: args[1]?.text(), + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}${options ? `, ${options}` : ''}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + { + oldBind: '$.patch', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[2], + method: 'PATCH', + body: args[1]?.text(), + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}${options ? `, ${options}` : ''}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + { + oldBind: '$.delete', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[1], + method: 'DELETE', + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}, ${options}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + { + oldBind: '$.head', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[1], + method: 'HEAD', + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}, ${options}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + { + oldBind: '$.options', + replaceFn: (args) => { + const url = args.length > 0 && args[0]; + const options = createOptions({ + oldOptions: args[1], + method: 'OPTIONS', + }); + return dedent.withOptions({ alignValues: true })` + fetch(${url.text()}, ${options}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null) + `; + }, + }, + ]; + +/** + * Generates options for the Fetch API based on the provided parameters. + * + * @param {Object} param0 - The parameters for creating options. + * @param {SgNode} [param0.oldOptions] - The old options node to extract headers from. + * @param {string} [param0.method] - The HTTP method to use (e.g., 'POST', 'GET'). + * @param {string} [param0.body] - The body content to include in the request. + * @returns {string} The generated options string for the Fetch API. + */ +const createOptions = ({ oldOptions, method, body }: CreateOptionsType) => { + if (!oldOptions && !method && !body) return ''; + const headers = oldOptions?.find({ + rule: { + kind: 'object', + inside: { + kind: 'pair', + has: { + kind: 'property_identifier', + field: 'key', + regex: 'headers', + }, + }, + }, + }); + + const options = []; + + if (method) { + options.push(`method: '${method}'`); + } + + if (headers) { + options.push(`headers: ${headers?.text()}`); + } + + if (body) { + options.push(`body: JSON.stringify(${body})`); + } + + if (options.length === 1) return `{ ${options.toString()} }`; + + return dedent.withOptions({ alignValues: true })`{ + ${options.join(`,${EOL}`)} + }`; +}; + +/** + * Transforms the AST root by replacing axios bindings with Fetch API calls. + * + * @param {SgRoot} root - The root of the AST to transform. + * @returns {string | null} The transformed source code or null if no changes were made. + */ +export default function transform(root: SgRoot): string | null { + const rootNode = root.root(); + const edits: Edit[] = []; + const linesToRemove: Range[] = []; + const bindsToReplace: BindingToReplace[] = []; + + const importRequireStatement = [ + ...getNodeRequireCalls(root, 'axios'), + ...getNodeImportStatements(root, 'axios'), + ]; + + if (!importRequireStatement.length) return null; + + for (const node of importRequireStatement) { + for (const update of updates) { + const bind = resolveBindingPath(node, update.oldBind); + + if (!bind) continue; + + bindsToReplace.push({ + rule: { + pattern: `${bind}($$$ARG)`, + }, + node, + binding: bind, + replaceFn: update.replaceFn, + }); + } + } + + for (const bind of bindsToReplace) { + const matches = rootNode.findAll({ + rule: bind.rule, + }); + + for (const match of matches) { + const argsAndCommaas = match.getMultipleMatches('ARG'); + const args = argsAndCommaas.filter((arg) => arg.text() !== ','); + + if (unsupportedMethods.includes(bind.binding.split('.').at(-1))) { + console.warn( + 'Un-migratable method has been found. Please revise this part of the code.', + ); + continue; + } + + const replace = match.replace(bind.replaceFn(args)); + edits.push(replace); + + const result = removeBinding(bind.node, bind.binding.split('.').at(0)); + + if (result?.lineToRemove) { + linesToRemove.push(result.lineToRemove); + } + + if (result?.edit) { + edits.push(result.edit); + } + } + } + + if (!edits.length) return null; + + const sourceCode = rootNode.commitEdits(edits); + + return removeLines(sourceCode, linesToRemove); +} diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/delete-with-config.js b/recipes/axios-to-whatwg-fetch/tests/expected/delete-with-config.js new file mode 100644 index 00000000..833410a4 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/delete-with-config.js @@ -0,0 +1,8 @@ + +const deletedTodo = await fetch('https://dummyjson.com/todos/1', { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' } +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nDELETE /todos1/1 ->', deletedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/delete.js b/recipes/axios-to-whatwg-fetch/tests/expected/delete.js new file mode 100644 index 00000000..911eaca1 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/delete.js @@ -0,0 +1,6 @@ +const base = 'https://dummyjson.com/todos/1'; + +const deletedTodo = await fetch(base, { method: 'DELETE' }) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nDELETE /todos ->', deletedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/get-with-config.js b/recipes/axios-to-whatwg-fetch/tests/expected/get-with-config.js new file mode 100644 index 00000000..d4718e39 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/get-with-config.js @@ -0,0 +1,6 @@ + +const all = await fetch("https://dummyjson.com/todos", { headers: { "Content-Type": "application/json" } }) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log("\nGET /todos ->", all.status); +console.log(`Preview: ${all.data.todos.length} todos`); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/get.js b/recipes/axios-to-whatwg-fetch/tests/expected/get.js new file mode 100644 index 00000000..045d0c94 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/get.js @@ -0,0 +1,7 @@ +const base = "https://dummyjson.com/todos"; + +const all = await fetch(base) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log("\nGET /todos ->", all.status); +console.log(`Preview: ${all.data.todos.length} todos`); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/head-with-config.js b/recipes/axios-to-whatwg-fetch/tests/expected/head-with-config.js new file mode 100644 index 00000000..cd2390ce --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/head-with-config.js @@ -0,0 +1,7 @@ + +const all = await fetch('https://dummyjson.com/todos', { + method: 'HEAD', + headers: { 'Content-Type': 'application/json' } +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/head.js b/recipes/axios-to-whatwg-fetch/tests/expected/head.js new file mode 100644 index 00000000..91b01d94 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/head.js @@ -0,0 +1,4 @@ + +const all = await fetch('https://dummyjson.com/todos', { method: 'HEAD' }) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/options-with-config.js b/recipes/axios-to-whatwg-fetch/tests/expected/options-with-config.js new file mode 100644 index 00000000..54afce4e --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/options-with-config.js @@ -0,0 +1,7 @@ + +const all = await fetch('https://dummyjson.com/todos', { + method: 'OPTIONS', + headers: { 'Content-Type': 'application/json' } +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/options.js b/recipes/axios-to-whatwg-fetch/tests/expected/options.js new file mode 100644 index 00000000..255c3c69 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/options.js @@ -0,0 +1,4 @@ + +const all = await fetch('https://dummyjson.com/todos', { method: 'OPTIONS' }) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/patch.js b/recipes/axios-to-whatwg-fetch/tests/expected/patch.js new file mode 100644 index 00000000..1b1ad0fc --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/patch.js @@ -0,0 +1,14 @@ + +// Unsupported method: axios.patch +const base = 'https://dummyjson.com/todos/1'; + +const patchedTodo = await fetch(base, { + method: 'PATCH', + body: JSON.stringify({ + todo: 'Updated todo', + completed: true, + }) +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nPATCH /todos/1 ->', patchedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/post-with-config.js b/recipes/axios-to-whatwg-fetch/tests/expected/post-with-config.js new file mode 100644 index 00000000..cff1e5d8 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/post-with-config.js @@ -0,0 +1,14 @@ +const base = 'https://dummyjson.com/todos/add'; + +const createdTodo = await fetch(base, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, + }) +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nPOST /todos/add ->', createdTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/post.js b/recipes/axios-to-whatwg-fetch/tests/expected/post.js new file mode 100644 index 00000000..3c34eec6 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/post.js @@ -0,0 +1,13 @@ +const base = 'https://dummyjson.com/todos/add'; + +const todoCreated = await fetch(base, { + method: 'POST', + body: JSON.stringify({ + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, + }) +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nPOST /todos ->', todoCreated); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/put-with-config.js b/recipes/axios-to-whatwg-fetch/tests/expected/put-with-config.js new file mode 100644 index 00000000..ae90c942 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/put-with-config.js @@ -0,0 +1,14 @@ +const base = 'https://dummyjson.com/todos/1'; + +const updatedTodo = await fetch(base, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, + }) +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nPUT /todos/1 ->', updatedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/put.js b/recipes/axios-to-whatwg-fetch/tests/expected/put.js new file mode 100644 index 00000000..329c12d2 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/put.js @@ -0,0 +1,13 @@ +const base = 'https://dummyjson.com/todos/1'; + +const updatedTodo = await fetch(base, { + method: 'PUT', + body: JSON.stringify({ + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, + }) +}) + .then(async (res) => Object.assign(res, { data: await res.json() })) + .catch(() => null); +console.log('\nPUT /todos/1 ->', updatedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/expected/request.js b/recipes/axios-to-whatwg-fetch/tests/expected/request.js new file mode 100644 index 00000000..fcec5de6 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/expected/request.js @@ -0,0 +1,14 @@ +import axios from 'axios'; + +// Unsupported method: axios.request +const base = 'https://dummyjson.com/todos/1'; + +const customRequest = await axios.request({ + url: base, + method: 'PATCH', + data: { + todo: 'Updated todo', + completed: true, + }, +}); +console.log('\nREQUEST /todos/1 ->', customRequest); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/delete-with-config.js b/recipes/axios-to-whatwg-fetch/tests/input/delete-with-config.js new file mode 100644 index 00000000..5219afa7 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/delete-with-config.js @@ -0,0 +1,6 @@ +import axios from 'axios'; + +const deletedTodo = await axios.delete('https://dummyjson.com/todos/1', { + headers: { 'Content-Type': 'application/json' }, +}); +console.log('\nDELETE /todos1/1 ->', deletedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/delete.js b/recipes/axios-to-whatwg-fetch/tests/input/delete.js new file mode 100644 index 00000000..95d91c94 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/delete.js @@ -0,0 +1,5 @@ +import axios from 'axios'; +const base = 'https://dummyjson.com/todos/1'; + +const deletedTodo = await axios.delete(base); +console.log('\nDELETE /todos ->', deletedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/get-with-config.js b/recipes/axios-to-whatwg-fetch/tests/input/get-with-config.js new file mode 100644 index 00000000..f6103278 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/get-with-config.js @@ -0,0 +1,7 @@ +import axios from "axios"; + +const all = await axios.get("https://dummyjson.com/todos", { + headers: { "Content-Type": "application/json" }, +}); +console.log("\nGET /todos ->", all.status); +console.log(`Preview: ${all.data.todos.length} todos`); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/get.js b/recipes/axios-to-whatwg-fetch/tests/input/get.js new file mode 100644 index 00000000..6efc338f --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/get.js @@ -0,0 +1,6 @@ +import axios from "axios"; +const base = "https://dummyjson.com/todos"; + +const all = await axios.get(base); +console.log("\nGET /todos ->", all.status); +console.log(`Preview: ${all.data.todos.length} todos`); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/head-with-config.js b/recipes/axios-to-whatwg-fetch/tests/input/head-with-config.js new file mode 100644 index 00000000..fea04ab6 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/head-with-config.js @@ -0,0 +1,5 @@ +import axios from 'axios'; + +const all = await axios.head('https://dummyjson.com/todos', { + headers: { 'Content-Type': 'application/json' }, +}); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/head.js b/recipes/axios-to-whatwg-fetch/tests/input/head.js new file mode 100644 index 00000000..af8e6368 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/head.js @@ -0,0 +1,3 @@ +import axios from 'axios'; + +const all = await axios.head('https://dummyjson.com/todos'); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/options-with-config.js b/recipes/axios-to-whatwg-fetch/tests/input/options-with-config.js new file mode 100644 index 00000000..11039542 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/options-with-config.js @@ -0,0 +1,5 @@ +import axios from 'axios'; + +const all = await axios.options('https://dummyjson.com/todos', { + headers: { 'Content-Type': 'application/json' }, +}); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/options.js b/recipes/axios-to-whatwg-fetch/tests/input/options.js new file mode 100644 index 00000000..9f46c4d4 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/options.js @@ -0,0 +1,3 @@ +import axios from 'axios'; + +const all = await axios.options('https://dummyjson.com/todos'); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/patch.js b/recipes/axios-to-whatwg-fetch/tests/input/patch.js new file mode 100644 index 00000000..e4e92c2d --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/patch.js @@ -0,0 +1,10 @@ +import axios from 'axios'; + +// Unsupported method: axios.patch +const base = 'https://dummyjson.com/todos/1'; + +const patchedTodo = await axios.patch(base, { + todo: 'Updated todo', + completed: true, +}); +console.log('\nPATCH /todos/1 ->', patchedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/post-with-config.js b/recipes/axios-to-whatwg-fetch/tests/input/post-with-config.js new file mode 100644 index 00000000..f887b607 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/post-with-config.js @@ -0,0 +1,15 @@ +import axios from 'axios'; +const base = 'https://dummyjson.com/todos/add'; + +const createdTodo = await axios.post( + base, + { + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, + }, + { + headers: { 'Content-Type': 'application/json' }, + }, +); +console.log('\nPOST /todos/add ->', createdTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/post.js b/recipes/axios-to-whatwg-fetch/tests/input/post.js new file mode 100644 index 00000000..2db408de --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/post.js @@ -0,0 +1,9 @@ +import axios from 'axios'; +const base = 'https://dummyjson.com/todos/add'; + +const todoCreated = await axios.post(base, { + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, +}); +console.log('\nPOST /todos ->', todoCreated); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/put-with-config.js b/recipes/axios-to-whatwg-fetch/tests/input/put-with-config.js new file mode 100644 index 00000000..efc0e103 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/put-with-config.js @@ -0,0 +1,15 @@ +import axios from 'axios'; +const base = 'https://dummyjson.com/todos/1'; + +const updatedTodo = await axios.put( + base, + { + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, + }, + { + headers: { 'Content-Type': 'application/json' }, + }, +); +console.log('\nPUT /todos/1 ->', updatedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/put.js b/recipes/axios-to-whatwg-fetch/tests/input/put.js new file mode 100644 index 00000000..d2c98b6d --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/put.js @@ -0,0 +1,9 @@ +import axios from 'axios'; +const base = 'https://dummyjson.com/todos/1'; + +const updatedTodo = await axios.put(base, { + todo: 'Use DummyJSON in the project', + completed: false, + userId: 5, +}); +console.log('\nPUT /todos/1 ->', updatedTodo); diff --git a/recipes/axios-to-whatwg-fetch/tests/input/request.js b/recipes/axios-to-whatwg-fetch/tests/input/request.js new file mode 100644 index 00000000..fcec5de6 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/tests/input/request.js @@ -0,0 +1,14 @@ +import axios from 'axios'; + +// Unsupported method: axios.request +const base = 'https://dummyjson.com/todos/1'; + +const customRequest = await axios.request({ + url: base, + method: 'PATCH', + data: { + todo: 'Updated todo', + completed: true, + }, +}); +console.log('\nREQUEST /todos/1 ->', customRequest); diff --git a/recipes/axios-to-whatwg-fetch/workflow.yaml b/recipes/axios-to-whatwg-fetch/workflow.yaml new file mode 100644 index 00000000..1efcc5c4 --- /dev/null +++ b/recipes/axios-to-whatwg-fetch/workflow.yaml @@ -0,0 +1,25 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/codemod-com/codemod/refs/heads/main/schemas/workflow.json + +version: "1" + +nodes: + - id: apply-transforms + name: Apply AST Transformations + type: automatic + steps: + - name: Replace `axios` with `fetch` + js-ast-grep: + js_file: src/workflow.ts + base_path: . + include: + - "**/*.cjs" + - "**/*.cts" + - "**/*.js" + - "**/*.jsx" + - "**/*.mjs" + - "**/*.mts" + - "**/*.ts" + - "**/*.tsx" + exclude: + - "**/node_modules/**" + language: typescript diff --git a/recipes/correct-ts-specifiers/src/fixtures/e2e/test.ts b/recipes/correct-ts-specifiers/src/fixtures/e2e/test.ts index 34df1387..8ce2f40b 100644 --- a/recipes/correct-ts-specifiers/src/fixtures/e2e/test.ts +++ b/recipes/correct-ts-specifiers/src/fixtures/e2e/test.ts @@ -3,17 +3,17 @@ import { URL } from 'node:url'; import { bar } from '@dep/bar'; import { foo } from 'foo'; -import { Bird } from './Bird'; +import { Bird } from './Bird/index.ts'; import { Cat } from './Cat.ts'; -import { Dog } from '…/Dog/index.mjs'; +import { Dog } from '…/Dog/index.mts'; import { baseUrl } from '#config.js'; -import { qux } from './qux.js'; +import { qux } from './qux.js/index.ts'; -export { Zed } from './zed'; +export type { Zed } from './zed.d.ts'; // should.js be unchanged -const nil = await import('./nil.js'); +const nil = await import('./nil.ts'); const bird = new Bird('Tweety'); const cat = new Cat('Milo');