From 2cf60d812c2608dfa9232d060b2f671822f74b39 Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 7 Nov 2026 13:34:25 -0800 Subject: [PATCH 01/10] build: upgrade App to TypeScript 7.1 beta --- package-lock.json | 185 +++++++++--------- package.json | 2 +- src/languages/es.ts | 2 +- tests/ui/BaseVerifyDomainPageTest.tsx | 4 +- tests/ui/DomainAccessRestrictedPageTest.tsx | 4 +- tests/ui/DomainAlreadyExistsPageTest.tsx | 4 +- tests/ui/OnboardingPurpose.tsx | 4 +- tests/ui/PersonalDetailsOnboarding.tsx | 2 +- tests/ui/WorkspaceOnboarding.tsx | 6 +- tests/unit/ReceiptAlternativeMethodsTest.tsx | 6 +- web/cloudflare-worker/package-lock.json | 186 ++++++++++--------- web/cloudflare-worker/package.json | 2 +- 12 files changed, 224 insertions(+), 183 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9d81f1ba241c..b7280d3e0581 100644 --- a/package-lock.json +++ b/package-lock.json @@ -291,7 +291,7 @@ "storybook-react-rsbuild": "^3.3.4", "svgo": "^4.1.0", "type-fest": "4.35.0", - "typescript": "^7.0.2", + "typescript": "7.1.0-beta", "typescript-eslint": "8.58.1", "wait-port": "^0.2.9", "workbox-core": "7.4.0", @@ -20807,9 +20807,9 @@ } }, "node_modules/@typescript/typescript-aix-ppc64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", - "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.1.0-beta.tgz", + "integrity": "sha512-DXDG+jIWxBHGdoO70tcHON33rCnSzsEZj9P13oyf/r/KEGIo6awsdSNVNjAw0uMwEpqRu75IrgCcW58YcVkZHg==", "cpu": [ "ppc64" ], @@ -20822,10 +20822,26 @@ "node": ">=16.20.0" } }, + "node_modules/@typescript/typescript-android-arm64": { + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-android-arm64/-/typescript-android-arm64-7.1.0-beta.tgz", + "integrity": "sha512-UZx6PxoQavuvaVTrSf3aw0Oy/J/uJpRylipA0kzc038jMvZXh0UtE8vnQp20FqtdgD5bhXZ0aJrRJyoTSUO58Q==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@typescript/typescript-darwin-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", - "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.1.0-beta.tgz", + "integrity": "sha512-q7mxblcyX0E7hXsY4k+Q6KWKK5mGmfVq+BDesdTgRVgjodk27aIxER77Zyj9+TgSTlCje8F7+JM6f6hl2BuNdw==", "cpu": [ "arm64" ], @@ -20839,9 +20855,9 @@ } }, "node_modules/@typescript/typescript-darwin-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", - "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.1.0-beta.tgz", + "integrity": "sha512-D5EYqG5Wm/guw9+0j8+Mn1Jbr/gfS/VtFTuoH8PJtFbcqeDaQGMulc7SW8q51QG6Y3wGOGX3nbsTOw6fIKWWAA==", "cpu": [ "x64" ], @@ -20855,9 +20871,9 @@ } }, "node_modules/@typescript/typescript-freebsd-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", - "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.1.0-beta.tgz", + "integrity": "sha512-9A0XX0tcnH9qPdolSKKzSjTb93dlB0UEcAJbHYEsrFX2fkFNSum9+dvkQQJ9vLc+7vxHetDPujq9Qe6wx+Xhow==", "cpu": [ "arm64" ], @@ -20871,9 +20887,9 @@ } }, "node_modules/@typescript/typescript-freebsd-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", - "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.1.0-beta.tgz", + "integrity": "sha512-EDf/dBHfY0TxX3NmUTek0hbffeyZlh3+ls6WQW3ICB8vYxwYLFnzg7wkgEUvl9eRlpiJ/nmc2S8arPMzINJ7rg==", "cpu": [ "x64" ], @@ -20887,9 +20903,9 @@ } }, "node_modules/@typescript/typescript-linux-arm": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", - "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.1.0-beta.tgz", + "integrity": "sha512-AvN9HmzOQh46G4312ooSgMHhbB7DtnLwg8D9mo324X49B7weMwtJrBG1MxT9IyI8kzDd7YRtW+1hWGun7WG7Qw==", "cpu": [ "arm" ], @@ -20903,9 +20919,9 @@ } }, "node_modules/@typescript/typescript-linux-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", - "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.1.0-beta.tgz", + "integrity": "sha512-hNqQBgQ5b5oGqN76QqbAU6CwYhJZ6iOcWc2kPTUs2OentgpxZktETziH6KIyRFJPaiBncognwn2ZLu3DNhHiWw==", "cpu": [ "arm64" ], @@ -20919,9 +20935,9 @@ } }, "node_modules/@typescript/typescript-linux-loong64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", - "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.1.0-beta.tgz", + "integrity": "sha512-xbUustlBrFflt2pNEyG5ye+d4WGPc/rfr2xK29MXeobPSw297JqwuKRrvbKM5o3fE/eUM/c+urVm6jQ3IaMVTw==", "cpu": [ "loong64" ], @@ -20935,9 +20951,9 @@ } }, "node_modules/@typescript/typescript-linux-mips64el": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", - "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.1.0-beta.tgz", + "integrity": "sha512-fS4/G7/0bXZ3NSq3nBMAXyg65I3tO4S7j08VNQ2g1aNC/qq79gE7Qne1BNjVsjMFfHBVBFjQYoTQCjmVU2iFlg==", "cpu": [ "mips64el" ], @@ -20951,9 +20967,9 @@ } }, "node_modules/@typescript/typescript-linux-ppc64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", - "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.1.0-beta.tgz", + "integrity": "sha512-0upCCxYPX8X6gOwkvfv+Ee7/ufyy7k97mTrGE0e5BxLlnUEa+w+rtgnv2dsKcxFdwdeZYVlZKCqgN/xCBV08+A==", "cpu": [ "ppc64" ], @@ -20967,9 +20983,9 @@ } }, "node_modules/@typescript/typescript-linux-riscv64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", - "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.1.0-beta.tgz", + "integrity": "sha512-0jKU9YRgOPRgiy5V/UWv37kB53PaTpIbwGCbbOMUPPAkrjVRNOIAJaz7Wa7s3H7mdDebX1LopUEPEJR+7Xa7zg==", "cpu": [ "riscv64" ], @@ -20983,9 +20999,9 @@ } }, "node_modules/@typescript/typescript-linux-s390x": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", - "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.1.0-beta.tgz", + "integrity": "sha512-j+d04s2kxaPQh6DLPb01sknAQfzsrdSZy15jBRHyLEZoaaQ19dVjbFRlkNVvYwUG7ZdP8+y5b/7RmPxdtHUKdQ==", "cpu": [ "s390x" ], @@ -20999,9 +21015,9 @@ } }, "node_modules/@typescript/typescript-linux-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", - "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.1.0-beta.tgz", + "integrity": "sha512-kcD51ihf+pnI5KT4uQQYG04OpwznaZYBlIQhDhl8fflFd27NktxXsUeguxZyvUMyWfum7IF2bBS6Jv7ox0AgFQ==", "cpu": [ "x64" ], @@ -21015,9 +21031,9 @@ } }, "node_modules/@typescript/typescript-netbsd-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", - "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.1.0-beta.tgz", + "integrity": "sha512-8W85YyoGK+rgGoSU9jJQUpELbrKcZcbdJk2jPNQ6UKDKI7ryPlPFTrV2jCE5RQuv4Tt96hMDTc33s4YfRrLFBA==", "cpu": [ "arm64" ], @@ -21031,9 +21047,9 @@ } }, "node_modules/@typescript/typescript-netbsd-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", - "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.1.0-beta.tgz", + "integrity": "sha512-iGsTE0ve2PG0AcyZinwAR0LnGEZhu/y4dceS0hZsYCaKdYETvQtvtlNWlmYKempsBr6C7Il7dMXHVk+VFm9u0g==", "cpu": [ "x64" ], @@ -21047,9 +21063,9 @@ } }, "node_modules/@typescript/typescript-openbsd-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", - "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.1.0-beta.tgz", + "integrity": "sha512-Z2FERdHB9fAKI+KGqdWn04O7uNlnB+VYiEm6INE1aZuzzLNd3KBqQ6IpKUJF2ny4GigGU6nVu75XD9tAUODPng==", "cpu": [ "arm64" ], @@ -21063,9 +21079,9 @@ } }, "node_modules/@typescript/typescript-openbsd-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", - "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.1.0-beta.tgz", + "integrity": "sha512-NVG3qeyCq/rJEOhaRjR3LsWgjrjo5vJW25omVLeHAVnpKIG4pE4cenwwKGR06DiJ1s0QTOymcJeKXMEpsiNBVg==", "cpu": [ "x64" ], @@ -21079,9 +21095,9 @@ } }, "node_modules/@typescript/typescript-sunos-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", - "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.1.0-beta.tgz", + "integrity": "sha512-ovAdoWXEn2B5/fyWG69pLU3FWmQwPWOjQV5TFbcxtpnOv+R3g8ySGHl1NYRq7Lb/GYdFDvaGcZ+SiU7iKuiMsA==", "cpu": [ "x64" ], @@ -21095,9 +21111,9 @@ } }, "node_modules/@typescript/typescript-win32-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", - "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.1.0-beta.tgz", + "integrity": "sha512-lJbo71IEOfL7ZZIZnArRaOLqjn9BAfoSzyUQn0aSjfSpDZL4ru0afUsdBfo6PQEjD6ssdmi0n/RwaPADX6bsvQ==", "cpu": [ "arm64" ], @@ -21111,9 +21127,9 @@ } }, "node_modules/@typescript/typescript-win32-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", - "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.1.0-beta.tgz", + "integrity": "sha512-UKcHvbybY2tDTvFW2b1vwiAvDlIRooo69lh8CXWVhOQejcvpM5+IgbiFW9EIwR5Wi5X/v+BdN7gdWbigBuJ3rw==", "cpu": [ "x64" ], @@ -42524,9 +42540,9 @@ } }, "node_modules/typescript": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", - "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.1.0-beta.tgz", + "integrity": "sha512-cjWjZAJDsmZzsNC5GwHNlW0YPM65jZa5oR0KyQcYX3KSVIfTvtBq//vewISyatKk4OLi5grZjHhIVE9fdrw1UQ==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -42536,26 +42552,27 @@ "node": ">=16.20.0" }, "optionalDependencies": { - "@typescript/typescript-aix-ppc64": "7.0.2", - "@typescript/typescript-darwin-arm64": "7.0.2", - "@typescript/typescript-darwin-x64": "7.0.2", - "@typescript/typescript-freebsd-arm64": "7.0.2", - "@typescript/typescript-freebsd-x64": "7.0.2", - "@typescript/typescript-linux-arm": "7.0.2", - "@typescript/typescript-linux-arm64": "7.0.2", - "@typescript/typescript-linux-loong64": "7.0.2", - "@typescript/typescript-linux-mips64el": "7.0.2", - "@typescript/typescript-linux-ppc64": "7.0.2", - "@typescript/typescript-linux-riscv64": "7.0.2", - "@typescript/typescript-linux-s390x": "7.0.2", - "@typescript/typescript-linux-x64": "7.0.2", - "@typescript/typescript-netbsd-arm64": "7.0.2", - "@typescript/typescript-netbsd-x64": "7.0.2", - "@typescript/typescript-openbsd-arm64": "7.0.2", - "@typescript/typescript-openbsd-x64": "7.0.2", - "@typescript/typescript-sunos-x64": "7.0.2", - "@typescript/typescript-win32-arm64": "7.0.2", - "@typescript/typescript-win32-x64": "7.0.2" + "@typescript/typescript-aix-ppc64": "7.1.0-beta", + "@typescript/typescript-android-arm64": "7.1.0-beta", + "@typescript/typescript-darwin-arm64": "7.1.0-beta", + "@typescript/typescript-darwin-x64": "7.1.0-beta", + "@typescript/typescript-freebsd-arm64": "7.1.0-beta", + "@typescript/typescript-freebsd-x64": "7.1.0-beta", + "@typescript/typescript-linux-arm": "7.1.0-beta", + "@typescript/typescript-linux-arm64": "7.1.0-beta", + "@typescript/typescript-linux-loong64": "7.1.0-beta", + "@typescript/typescript-linux-mips64el": "7.1.0-beta", + "@typescript/typescript-linux-ppc64": "7.1.0-beta", + "@typescript/typescript-linux-riscv64": "7.1.0-beta", + "@typescript/typescript-linux-s390x": "7.1.0-beta", + "@typescript/typescript-linux-x64": "7.1.0-beta", + "@typescript/typescript-netbsd-arm64": "7.1.0-beta", + "@typescript/typescript-netbsd-x64": "7.1.0-beta", + "@typescript/typescript-openbsd-arm64": "7.1.0-beta", + "@typescript/typescript-openbsd-x64": "7.1.0-beta", + "@typescript/typescript-sunos-x64": "7.1.0-beta", + "@typescript/typescript-win32-arm64": "7.1.0-beta", + "@typescript/typescript-win32-x64": "7.1.0-beta" } }, "node_modules/typescript-eslint": { diff --git a/package.json b/package.json index bd68e69f2e3a..e607a3d26c7b 100644 --- a/package.json +++ b/package.json @@ -367,7 +367,7 @@ "storybook-react-rsbuild": "^3.3.4", "svgo": "^4.1.0", "type-fest": "4.35.0", - "typescript": "^7.0.2", + "typescript": "7.1.0-beta", "typescript-eslint": "8.58.1", "wait-port": "^0.2.9", "workbox-core": "7.4.0", diff --git a/src/languages/es.ts b/src/languages/es.ts index 3ee2b056b96b..d65e9755ab2e 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1434,7 +1434,7 @@ const translations: TranslationDeepObject = { date: 'Fecha', taxAmount: 'Importe del impuesto', taxRate: 'Tasa de impuesto', - approve: ({formattedAmount} = {}) => (formattedAmount ? `Aprobar ${formattedAmount}` : 'Aprobar'), + approve: ({formattedAmount}: {formattedAmount?: string} = {}) => (formattedAmount ? `Aprobar ${formattedAmount}` : 'Aprobar'), approved: 'Aprobado', cash: 'Efectivo', card: 'Tarjeta', diff --git a/tests/ui/BaseVerifyDomainPageTest.tsx b/tests/ui/BaseVerifyDomainPageTest.tsx index 7b7046efba9e..2b23b894f522 100644 --- a/tests/ui/BaseVerifyDomainPageTest.tsx +++ b/tests/ui/BaseVerifyDomainPageTest.tsx @@ -41,7 +41,9 @@ const DOMAIN_ADMIN_ACCESS = { const apiReadSpy = jest.spyOn(API, 'read').mockImplementation(() => {}); const navigateSpy = jest.spyOn(Navigation, 'navigate').mockImplementation(() => {}); // Runs the follow-up right away so the redirect target can be asserted without a real dismiss transition -const dismissModalSpy = jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); +const dismissModalSpy = jest + .spyOn(Navigation, 'dismissModal') + .mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); const Stack = createPlatformStackNavigator(); diff --git a/tests/ui/DomainAccessRestrictedPageTest.tsx b/tests/ui/DomainAccessRestrictedPageTest.tsx index 4265f83a9763..fba7fb8a1752 100644 --- a/tests/ui/DomainAccessRestrictedPageTest.tsx +++ b/tests/ui/DomainAccessRestrictedPageTest.tsx @@ -49,7 +49,9 @@ const DOMAIN_ADMIN_ACCESS = { const apiWriteSpy = jest.spyOn(API, 'write').mockImplementation(() => Promise.resolve()); const navigateSpy = jest.spyOn(Navigation, 'navigate').mockImplementation(() => {}); // Runs the follow-up right away so the redirect target can be asserted without a real dismiss transition -const dismissModalSpy = jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); +const dismissModalSpy = jest + .spyOn(Navigation, 'dismissModal') + .mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); const Stack = createPlatformStackNavigator(); diff --git a/tests/ui/DomainAlreadyExistsPageTest.tsx b/tests/ui/DomainAlreadyExistsPageTest.tsx index cf908fef6047..d2eab86849ae 100644 --- a/tests/ui/DomainAlreadyExistsPageTest.tsx +++ b/tests/ui/DomainAlreadyExistsPageTest.tsx @@ -36,7 +36,9 @@ const apiWriteSpy = jest.spyOn(API, 'write').mockImplementation(() => Promise.re const goBackSpy = jest.spyOn(Navigation, 'goBack').mockImplementation(() => {}); const navigateSpy = jest.spyOn(Navigation, 'navigate').mockImplementation(() => {}); // Runs the follow-up right away so the redirect target can be asserted without a real dismiss transition -const dismissModalSpy = jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); +const dismissModalSpy = jest + .spyOn(Navigation, 'dismissModal') + .mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); const Stack = createPlatformStackNavigator(); diff --git a/tests/ui/OnboardingPurpose.tsx b/tests/ui/OnboardingPurpose.tsx index 5b51c408c751..c4e62390bd27 100644 --- a/tests/ui/OnboardingPurpose.tsx +++ b/tests/ui/OnboardingPurpose.tsx @@ -275,7 +275,7 @@ describe('OnboardingPurpose Page', () => { }); it('should create a Submit workspace when user selects EMPLOYER and is from private domain with name set', async () => { - jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); + jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); jest.spyOn(Navigation, 'setNavigationActionToMicrotaskQueue').mockImplementation((callback: () => void) => callback()); const testEmail = 'test@user.com'; @@ -322,7 +322,7 @@ describe('OnboardingPurpose Page', () => { }); it('should create a Submit workspace from Purpose when EMPLOYER is selected and personal details already exist', async () => { - jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); + jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); jest.spyOn(Navigation, 'setNavigationActionToMicrotaskQueue').mockImplementation((callback: () => void) => callback()); await TestHelper.signInWithTestUser(); diff --git a/tests/ui/PersonalDetailsOnboarding.tsx b/tests/ui/PersonalDetailsOnboarding.tsx index fe6aa679405b..64e2df621605 100644 --- a/tests/ui/PersonalDetailsOnboarding.tsx +++ b/tests/ui/PersonalDetailsOnboarding.tsx @@ -220,7 +220,7 @@ describe('OnboardingPersonalDetails Page', () => { }); it('should create a Submit workspace when submitting form with EMPLOYER and public domain', async () => { - jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); + jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); jest.spyOn(Navigation, 'setNavigationActionToMicrotaskQueue').mockImplementation((callback: () => void) => callback()); await TestHelper.signInWithTestUser(); diff --git a/tests/ui/WorkspaceOnboarding.tsx b/tests/ui/WorkspaceOnboarding.tsx index 00ddc16ab338..45ec00a57b6a 100644 --- a/tests/ui/WorkspaceOnboarding.tsx +++ b/tests/ui/WorkspaceOnboarding.tsx @@ -1142,7 +1142,7 @@ describe('OnboardingWorkspaces Page', () => { }); it('should create a Submit workspace when skip is pressed with EMPLOYER purpose', async () => { - jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); + jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); jest.spyOn(Navigation, 'setNavigationActionToMicrotaskQueue').mockImplementation((callback: () => void) => callback()); await TestHelper.signInWithTestUser(); @@ -1201,7 +1201,7 @@ describe('OnboardingWorkspaces Page', () => { }); it('should not set RHP_ADMINS_ROOM when joining a Submit workspace with the Join Workspace intent', async () => { - jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); + jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); jest.spyOn(Navigation, 'setNavigationActionToMicrotaskQueue').mockImplementation((callback: () => void) => callback()); await TestHelper.signInWithTestUser(); @@ -1266,7 +1266,7 @@ describe('OnboardingWorkspaces Page', () => { }); it('should complete onboarding without passing the joined workspace policyID and open Spend > Expenses with the side panel closed', async () => { - jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition} = {}) => afterTransition?.()); + jest.spyOn(Navigation, 'dismissModal').mockImplementation(({afterTransition}: NonNullable[0]> = {}) => afterTransition?.()); jest.spyOn(Navigation, 'setNavigationActionToMicrotaskQueue').mockImplementation((callback: () => void) => callback()); await TestHelper.signInWithTestUser(); diff --git a/tests/unit/ReceiptAlternativeMethodsTest.tsx b/tests/unit/ReceiptAlternativeMethodsTest.tsx index d34f7042353a..00d2dab50a07 100644 --- a/tests/unit/ReceiptAlternativeMethodsTest.tsx +++ b/tests/unit/ReceiptAlternativeMethodsTest.tsx @@ -51,9 +51,9 @@ jest.mock('@hooks/useLocalize', () => const translations: Record) => string)> = {}; translations['receipt.alternativeMethodsTitle'] = 'Other ways to add receipts:'; translations['receipt.alternativeMethodsDownloadApp'] = 'Download the app to scan from your phone'; - translations['receipt.alternativeMethodsForwardReceipts'] = ({email} = {}) => `Forward receipts to ${email}`; - translations['receipt.alternativeMethodsAddPhoneNumber'] = ({phoneNumber} = {}) => `Add your number to text receipts to ${phoneNumber}`; - translations['receipt.alternativeMethodsTextReceipts'] = ({phoneNumber} = {}) => `Text receipts to ${phoneNumber} (US numbers only)`; + translations['receipt.alternativeMethodsForwardReceipts'] = ({email}: Record = {}) => `Forward receipts to ${email}`; + translations['receipt.alternativeMethodsAddPhoneNumber'] = ({phoneNumber}: Record = {}) => `Add your number to text receipts to ${phoneNumber}`; + translations['receipt.alternativeMethodsTextReceipts'] = ({phoneNumber}: Record = {}) => `Text receipts to ${phoneNumber} (US numbers only)`; const translation = translations[key]; if (typeof translation === 'function') { diff --git a/web/cloudflare-worker/package-lock.json b/web/cloudflare-worker/package-lock.json index c77eaf3468cc..ce73b45b6737 100644 --- a/web/cloudflare-worker/package-lock.json +++ b/web/cloudflare-worker/package-lock.json @@ -10,7 +10,7 @@ "devDependencies": { "@types/node": "24.19.0", "@types/ws": "8.18.1", - "typescript": "7.0.2", + "typescript": "7.1.0-beta", "wrangler": "4.152.0", "ws": "8.21.0" } @@ -1265,9 +1265,9 @@ } }, "node_modules/@typescript/typescript-aix-ppc64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz", - "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.1.0-beta.tgz", + "integrity": "sha512-DXDG+jIWxBHGdoO70tcHON33rCnSzsEZj9P13oyf/r/KEGIo6awsdSNVNjAw0uMwEpqRu75IrgCcW58YcVkZHg==", "cpu": [ "ppc64" ], @@ -1281,10 +1281,27 @@ "node": ">=16.20.0" } }, + "node_modules/@typescript/typescript-android-arm64": { + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-android-arm64/-/typescript-android-arm64-7.1.0-beta.tgz", + "integrity": "sha512-UZx6PxoQavuvaVTrSf3aw0Oy/J/uJpRylipA0kzc038jMvZXh0UtE8vnQp20FqtdgD5bhXZ0aJrRJyoTSUO58Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/@typescript/typescript-darwin-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz", - "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.1.0-beta.tgz", + "integrity": "sha512-q7mxblcyX0E7hXsY4k+Q6KWKK5mGmfVq+BDesdTgRVgjodk27aIxER77Zyj9+TgSTlCje8F7+JM6f6hl2BuNdw==", "cpu": [ "arm64" ], @@ -1299,9 +1316,9 @@ } }, "node_modules/@typescript/typescript-darwin-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz", - "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.1.0-beta.tgz", + "integrity": "sha512-D5EYqG5Wm/guw9+0j8+Mn1Jbr/gfS/VtFTuoH8PJtFbcqeDaQGMulc7SW8q51QG6Y3wGOGX3nbsTOw6fIKWWAA==", "cpu": [ "x64" ], @@ -1316,9 +1333,9 @@ } }, "node_modules/@typescript/typescript-freebsd-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz", - "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.1.0-beta.tgz", + "integrity": "sha512-9A0XX0tcnH9qPdolSKKzSjTb93dlB0UEcAJbHYEsrFX2fkFNSum9+dvkQQJ9vLc+7vxHetDPujq9Qe6wx+Xhow==", "cpu": [ "arm64" ], @@ -1333,9 +1350,9 @@ } }, "node_modules/@typescript/typescript-freebsd-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz", - "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.1.0-beta.tgz", + "integrity": "sha512-EDf/dBHfY0TxX3NmUTek0hbffeyZlh3+ls6WQW3ICB8vYxwYLFnzg7wkgEUvl9eRlpiJ/nmc2S8arPMzINJ7rg==", "cpu": [ "x64" ], @@ -1350,9 +1367,9 @@ } }, "node_modules/@typescript/typescript-linux-arm": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz", - "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.1.0-beta.tgz", + "integrity": "sha512-AvN9HmzOQh46G4312ooSgMHhbB7DtnLwg8D9mo324X49B7weMwtJrBG1MxT9IyI8kzDd7YRtW+1hWGun7WG7Qw==", "cpu": [ "arm" ], @@ -1367,9 +1384,9 @@ } }, "node_modules/@typescript/typescript-linux-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz", - "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.1.0-beta.tgz", + "integrity": "sha512-hNqQBgQ5b5oGqN76QqbAU6CwYhJZ6iOcWc2kPTUs2OentgpxZktETziH6KIyRFJPaiBncognwn2ZLu3DNhHiWw==", "cpu": [ "arm64" ], @@ -1384,9 +1401,9 @@ } }, "node_modules/@typescript/typescript-linux-loong64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz", - "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.1.0-beta.tgz", + "integrity": "sha512-xbUustlBrFflt2pNEyG5ye+d4WGPc/rfr2xK29MXeobPSw297JqwuKRrvbKM5o3fE/eUM/c+urVm6jQ3IaMVTw==", "cpu": [ "loong64" ], @@ -1401,9 +1418,9 @@ } }, "node_modules/@typescript/typescript-linux-mips64el": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz", - "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.1.0-beta.tgz", + "integrity": "sha512-fS4/G7/0bXZ3NSq3nBMAXyg65I3tO4S7j08VNQ2g1aNC/qq79gE7Qne1BNjVsjMFfHBVBFjQYoTQCjmVU2iFlg==", "cpu": [ "mips64el" ], @@ -1418,9 +1435,9 @@ } }, "node_modules/@typescript/typescript-linux-ppc64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz", - "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.1.0-beta.tgz", + "integrity": "sha512-0upCCxYPX8X6gOwkvfv+Ee7/ufyy7k97mTrGE0e5BxLlnUEa+w+rtgnv2dsKcxFdwdeZYVlZKCqgN/xCBV08+A==", "cpu": [ "ppc64" ], @@ -1435,9 +1452,9 @@ } }, "node_modules/@typescript/typescript-linux-riscv64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz", - "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.1.0-beta.tgz", + "integrity": "sha512-0jKU9YRgOPRgiy5V/UWv37kB53PaTpIbwGCbbOMUPPAkrjVRNOIAJaz7Wa7s3H7mdDebX1LopUEPEJR+7Xa7zg==", "cpu": [ "riscv64" ], @@ -1452,9 +1469,9 @@ } }, "node_modules/@typescript/typescript-linux-s390x": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz", - "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.1.0-beta.tgz", + "integrity": "sha512-j+d04s2kxaPQh6DLPb01sknAQfzsrdSZy15jBRHyLEZoaaQ19dVjbFRlkNVvYwUG7ZdP8+y5b/7RmPxdtHUKdQ==", "cpu": [ "s390x" ], @@ -1469,9 +1486,9 @@ } }, "node_modules/@typescript/typescript-linux-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz", - "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.1.0-beta.tgz", + "integrity": "sha512-kcD51ihf+pnI5KT4uQQYG04OpwznaZYBlIQhDhl8fflFd27NktxXsUeguxZyvUMyWfum7IF2bBS6Jv7ox0AgFQ==", "cpu": [ "x64" ], @@ -1486,9 +1503,9 @@ } }, "node_modules/@typescript/typescript-netbsd-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz", - "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.1.0-beta.tgz", + "integrity": "sha512-8W85YyoGK+rgGoSU9jJQUpELbrKcZcbdJk2jPNQ6UKDKI7ryPlPFTrV2jCE5RQuv4Tt96hMDTc33s4YfRrLFBA==", "cpu": [ "arm64" ], @@ -1503,9 +1520,9 @@ } }, "node_modules/@typescript/typescript-netbsd-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz", - "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.1.0-beta.tgz", + "integrity": "sha512-iGsTE0ve2PG0AcyZinwAR0LnGEZhu/y4dceS0hZsYCaKdYETvQtvtlNWlmYKempsBr6C7Il7dMXHVk+VFm9u0g==", "cpu": [ "x64" ], @@ -1520,9 +1537,9 @@ } }, "node_modules/@typescript/typescript-openbsd-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz", - "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.1.0-beta.tgz", + "integrity": "sha512-Z2FERdHB9fAKI+KGqdWn04O7uNlnB+VYiEm6INE1aZuzzLNd3KBqQ6IpKUJF2ny4GigGU6nVu75XD9tAUODPng==", "cpu": [ "arm64" ], @@ -1537,9 +1554,9 @@ } }, "node_modules/@typescript/typescript-openbsd-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz", - "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.1.0-beta.tgz", + "integrity": "sha512-NVG3qeyCq/rJEOhaRjR3LsWgjrjo5vJW25omVLeHAVnpKIG4pE4cenwwKGR06DiJ1s0QTOymcJeKXMEpsiNBVg==", "cpu": [ "x64" ], @@ -1554,9 +1571,9 @@ } }, "node_modules/@typescript/typescript-sunos-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz", - "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.1.0-beta.tgz", + "integrity": "sha512-ovAdoWXEn2B5/fyWG69pLU3FWmQwPWOjQV5TFbcxtpnOv+R3g8ySGHl1NYRq7Lb/GYdFDvaGcZ+SiU7iKuiMsA==", "cpu": [ "x64" ], @@ -1571,9 +1588,9 @@ } }, "node_modules/@typescript/typescript-win32-arm64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz", - "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.1.0-beta.tgz", + "integrity": "sha512-lJbo71IEOfL7ZZIZnArRaOLqjn9BAfoSzyUQn0aSjfSpDZL4ru0afUsdBfo6PQEjD6ssdmi0n/RwaPADX6bsvQ==", "cpu": [ "arm64" ], @@ -1588,9 +1605,9 @@ } }, "node_modules/@typescript/typescript-win32-x64": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz", - "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.1.0-beta.tgz", + "integrity": "sha512-UKcHvbybY2tDTvFW2b1vwiAvDlIRooo69lh8CXWVhOQejcvpM5+IgbiFW9EIwR5Wi5X/v+BdN7gdWbigBuJ3rw==", "cpu": [ "x64" ], @@ -1829,9 +1846,9 @@ "optional": true }, "node_modules/typescript": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", - "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==", + "version": "7.1.0-beta", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.1.0-beta.tgz", + "integrity": "sha512-cjWjZAJDsmZzsNC5GwHNlW0YPM65jZa5oR0KyQcYX3KSVIfTvtBq//vewISyatKk4OLi5grZjHhIVE9fdrw1UQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1841,26 +1858,27 @@ "node": ">=16.20.0" }, "optionalDependencies": { - "@typescript/typescript-aix-ppc64": "7.0.2", - "@typescript/typescript-darwin-arm64": "7.0.2", - "@typescript/typescript-darwin-x64": "7.0.2", - "@typescript/typescript-freebsd-arm64": "7.0.2", - "@typescript/typescript-freebsd-x64": "7.0.2", - "@typescript/typescript-linux-arm": "7.0.2", - "@typescript/typescript-linux-arm64": "7.0.2", - "@typescript/typescript-linux-loong64": "7.0.2", - "@typescript/typescript-linux-mips64el": "7.0.2", - "@typescript/typescript-linux-ppc64": "7.0.2", - "@typescript/typescript-linux-riscv64": "7.0.2", - "@typescript/typescript-linux-s390x": "7.0.2", - "@typescript/typescript-linux-x64": "7.0.2", - "@typescript/typescript-netbsd-arm64": "7.0.2", - "@typescript/typescript-netbsd-x64": "7.0.2", - "@typescript/typescript-openbsd-arm64": "7.0.2", - "@typescript/typescript-openbsd-x64": "7.0.2", - "@typescript/typescript-sunos-x64": "7.0.2", - "@typescript/typescript-win32-arm64": "7.0.2", - "@typescript/typescript-win32-x64": "7.0.2" + "@typescript/typescript-aix-ppc64": "7.1.0-beta", + "@typescript/typescript-android-arm64": "7.1.0-beta", + "@typescript/typescript-darwin-arm64": "7.1.0-beta", + "@typescript/typescript-darwin-x64": "7.1.0-beta", + "@typescript/typescript-freebsd-arm64": "7.1.0-beta", + "@typescript/typescript-freebsd-x64": "7.1.0-beta", + "@typescript/typescript-linux-arm": "7.1.0-beta", + "@typescript/typescript-linux-arm64": "7.1.0-beta", + "@typescript/typescript-linux-loong64": "7.1.0-beta", + "@typescript/typescript-linux-mips64el": "7.1.0-beta", + "@typescript/typescript-linux-ppc64": "7.1.0-beta", + "@typescript/typescript-linux-riscv64": "7.1.0-beta", + "@typescript/typescript-linux-s390x": "7.1.0-beta", + "@typescript/typescript-linux-x64": "7.1.0-beta", + "@typescript/typescript-netbsd-arm64": "7.1.0-beta", + "@typescript/typescript-netbsd-x64": "7.1.0-beta", + "@typescript/typescript-openbsd-arm64": "7.1.0-beta", + "@typescript/typescript-openbsd-x64": "7.1.0-beta", + "@typescript/typescript-sunos-x64": "7.1.0-beta", + "@typescript/typescript-win32-arm64": "7.1.0-beta", + "@typescript/typescript-win32-x64": "7.1.0-beta" } }, "node_modules/undici": { diff --git a/web/cloudflare-worker/package.json b/web/cloudflare-worker/package.json index d56e92d0554d..1a72d633a080 100644 --- a/web/cloudflare-worker/package.json +++ b/web/cloudflare-worker/package.json @@ -16,7 +16,7 @@ "devDependencies": { "@types/node": "24.19.0", "@types/ws": "8.18.1", - "typescript": "7.0.2", + "typescript": "7.1.0-beta", "wrangler": "4.152.0", "ws": "8.21.0" } From f4f20caf4c779bf30dd1e2afdcb72f57ab856350 Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 7 Nov 2026 13:40:34 -0800 Subject: [PATCH 02/10] docs: correct compiler config parsing comment --- scripts/typecheck.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/typecheck.ts b/scripts/typecheck.ts index d7f6f7e28862..5e98b8d1bf70 100644 --- a/scripts/typecheck.ts +++ b/scripts/typecheck.ts @@ -55,8 +55,7 @@ function readReferencePaths(config: unknown): string[] { /** * The projects the root solution references, relative to the repo root. * - * `--showConfig` is how we read them: the root config carries comments, so it is not plain JSON, and - * TypeScript 7 does not yet expose a compiler API we could parse it with. + * `--showConfig` handles comments in the root config and follows the compiler's config parsing rules. */ async function getReferencedProjects(): Promise { const result = await $`${tsc} -p tsconfig.json --showConfig`.cwd(projectRoot).quiet().nothrow(); From f7cc5962d853a1d1977de8b33fe04e87bf00bbff Mon Sep 17 00:00:00 2001 From: rory Date: Sat, 7 Nov 2026 13:47:30 -0800 Subject: [PATCH 03/10] chore: checkpoint WIP native scanner migration LOCAL WIP only; not ready to merge, push, or open as a PR. Use the native async API and dispose compiler/source leases. Move scanner tests to Bun. The beta sync API crashes under Bun at stdout._handle.fd. Latest full scanner run: 14/16 Bun cases pass. Malformed-TypeScript and directory cases time out in the full run, although isolated error-path tests pass. These failures remain unresolved. Bun/Jest project typechecks, changed-file lint/spelling, formatting, and staged self-review pass. No claim of complete TS6 removal. --- scripts/findUnusedStyles.ts | 75 ++++++----- .../findUnusedStyles.test.ts} | 121 +++++++++++------- 2 files changed, 116 insertions(+), 80 deletions(-) rename tests/{unit/findUnusedStylesTest.ts => tooling/findUnusedStyles.test.ts} (73%) diff --git a/scripts/findUnusedStyles.ts b/scripts/findUnusedStyles.ts index 4d4a8a3c6db4..08de349e3fab 100644 --- a/scripts/findUnusedStyles.ts +++ b/scripts/findUnusedStyles.ts @@ -1,9 +1,9 @@ -import * as ts from '@typescript/typescript6'; import * as fs from 'fs'; import {globSync} from 'glob'; import * as path from 'path'; - -import TSCompilerUtils from './utils/TSCompilerUtils'; +import * as ts from 'typescript/ast'; +// eslint-disable-next-line rulesdir/no-api-in-views -- This is TypeScript's compiler client, not Expensify's HTTP API. +import {API as CompilerAPI} from 'typescript/async'; type StyleDefinition = { key: string; @@ -29,22 +29,23 @@ class ComprehensiveStylesFinder { this.rootDir = rootDir; } - public findUnusedStyles(): StyleDefinition[] { + public async findUnusedStyles(): Promise { + await using api = new CompilerAPI(); console.log('🔍 Step 1: Finding all style definitions...'); - this.findAllStyleDefinitions(); + await this.findAllStyleDefinitions(api); console.log(`📊 Found ${this.styleDefinitions.size} style definitions`); console.log('🔍 Step 2: Loading all file contents...'); this.loadAllFileContents(); console.log('🔍 Step 3: Comprehensive style usage analysis...'); - this.findStyleUsagesComprehensive(); + await this.findStyleUsagesComprehensive(api); console.log('📊 Step 4: Identifying unused styles...'); return this.getUnusedStyles(); } - private findAllStyleDefinitions(): void { + private async findAllStyleDefinitions(api: CompilerAPI): Promise { const styleFiles = globSync(`src/styles/${STYLE_FILE_EXTENSIONS}`, { cwd: this.rootDir, ignore: EXCLUDED_STYLE_DIRS, @@ -57,9 +58,9 @@ class ComprehensiveStylesFinder { const fullPath = path.join(this.rootDir, file); try { const fileContent = fs.readFileSync(fullPath, 'utf8'); - const sourceFile = ts.createSourceFile(fullPath, fileContent, ts.ScriptTarget.Latest, true); + await using retained = await api.createSourceFile(fullPath, fileContent); styleFileContents.set(file, fileContent); - this.extractStyleKeysFromFile(sourceFile, file); + this.extractStyleKeysFromFile(retained.sourceFile, file); } catch (error) { console.warn(`Warning: Could not read style file ${file}:`, error); } @@ -68,7 +69,7 @@ class ComprehensiveStylesFinder { // Spread crediting must run after every style file has registered its definitions, or glob order decides which spreads count. const indexContent = styleFileContents.get('src/styles/index.ts'); if (indexContent) { - this.checkSpreadPatternsInStylesFile(indexContent, 'src/styles/index.ts'); + await this.checkSpreadPatternsInStylesFile(indexContent, 'src/styles/index.ts', api); } } @@ -113,7 +114,7 @@ class ComprehensiveStylesFinder { this.extractStyleDefinitionFromNode(node, sourceFile, file); } - ts.forEachChild(node, visit); + node.forEachChild(visit); }; visit(sourceFile); @@ -137,7 +138,7 @@ class ComprehensiveStylesFinder { private extractStyleDefinitionsFromObject(objectLiteral: ts.ObjectLiteralExpression, sourceFile: ts.SourceFile, file: string): void { for (const property of objectLiteral.properties) { if (ts.isPropertyAssignment(property) || ts.isMethodDeclaration(property)) { - const key = TSCompilerUtils.extractKeyFromPropertyNode(property); + const key = this.extractStyleKey(property); if (key && this.isStyleKey(key)) { try { @@ -185,7 +186,7 @@ class ComprehensiveStylesFinder { private extractStyleDefinitionFromNode(node: ts.Node, sourceFile: ts.SourceFile, file: string): void { // Look for object literal properties (style definitions) if (ts.isPropertyAssignment(node) || ts.isMethodDeclaration(node)) { - const key = TSCompilerUtils.extractKeyFromPropertyNode(node); + const key = this.extractStyleKey(node); if (key && this.isStyleKey(key)) { try { @@ -273,13 +274,20 @@ class ComprehensiveStylesFinder { } } - private findStyleUsagesComprehensive(): void { + private extractStyleKey(node: ts.PropertyAssignment | ts.MethodDeclaration): string | undefined { + if (ts.isIdentifier(node.name) || (ts.isPropertyAssignment(node) && ts.isStringLiteral(node.name))) { + return node.name.text; + } + return undefined; + } + + private async findStyleUsagesComprehensive(api: CompilerAPI): Promise { const allFiles = Array.from(this.fileContents.entries()); console.log(`Analyzing usage patterns for ${this.styleDefinitions.size} style keys across ${allFiles.length} files...`); let processedFiles = 0; for (const [file, content] of allFiles) { - this.analyzeFileForStyleUsage(file, content); + await this.analyzeFileForStyleUsage(file, content, api); processedFiles++; // Early termination: if all styles are found, stop processing @@ -297,10 +305,11 @@ class ComprehensiveStylesFinder { console.log(` Completed analysis.`); } - private analyzeFileForStyleUsage(file: string, content: string): void { + private async analyzeFileForStyleUsage(file: string, content: string, api: CompilerAPI): Promise { // Use TypeScript AST to get content without comments try { - const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true); + await using retained = await api.createSourceFile(path.join(this.rootDir, file), content); + const sourceFile = retained.sourceFile; // Use getText() to get content without comments const cleanContent = sourceFile.getText(); @@ -308,7 +317,7 @@ class ComprehensiveStylesFinder { // Check individual style usage patterns - iterate over current definitions const keysToCheck = Array.from(this.styleDefinitions.keys()); for (const key of keysToCheck) { - if (this.isStyleUsedInContent(key, cleanContent, file)) { + if (await this.isStyleUsedInContent(key, cleanContent, file, api)) { // Remove the key from definitions since it's used this.styleDefinitions.delete(key); } @@ -318,16 +327,17 @@ class ComprehensiveStylesFinder { console.warn(`Warning: Could not parse ${file} with AST, using original content`); const keysToCheck = Array.from(this.styleDefinitions.keys()); for (const key of keysToCheck) { - if (this.isStyleUsedInContent(key, content, file)) { + if (await this.isStyleUsedInContent(key, content, file, api)) { this.styleDefinitions.delete(key); } } } } - private checkSpreadPatternsInStylesFile(content: string, file: string): void { + private async checkSpreadPatternsInStylesFile(content: string, file: string, api: CompilerAPI): Promise { try { - const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true); + await using retained = await api.createSourceFile(path.join(this.rootDir, file), content); + const sourceFile = retained.sourceFile; const visit = (node: ts.Node) => { // Handle object spread assignments (in object literals) @@ -340,7 +350,7 @@ class ComprehensiveStylesFinder { this.handleSpreadExpression(node.expression); } - ts.forEachChild(node, visit); + node.forEachChild(visit); }; visit(sourceFile); @@ -409,20 +419,21 @@ class ComprehensiveStylesFinder { this.styleDefinitions.delete(name); } } - ts.forEachChild(node, visit); + node.forEachChild(visit); }; visit(expression); } - private isStyleUsedInContent(key: string, content: string, file: string): boolean { + private async isStyleUsedInContent(key: string, content: string, file: string, api: CompilerAPI): Promise { // Fast check: if the key doesn't appear at all in the content, it's not used if (!content.includes(key)) { return false; } try { - const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true); + await using retained = await api.createSourceFile(path.join(this.rootDir, file), content); + const sourceFile = retained.sourceFile; let found = false; @@ -443,22 +454,22 @@ class ComprehensiveStylesFinder { const parent = node.parent; if (ts.isPropertyAssignment(parent) && parent.name === node) { // This is a property name, not a usage - ts.forEachChild(node, visit); + node.forEachChild(visit); return; } if (ts.isMethodDeclaration(parent) && parent.name === node) { // This is a method name, not a usage - ts.forEachChild(node, visit); + node.forEachChild(visit); return; } if (ts.isGetAccessorDeclaration(parent) && parent.name === node) { // This is a getter name, not a usage - ts.forEachChild(node, visit); + node.forEachChild(visit); return; } if (ts.isSetAccessorDeclaration(parent) && parent.name === node) { // This is a setter name, not a usage - ts.forEachChild(node, visit); + node.forEachChild(visit); return; } @@ -497,7 +508,7 @@ class ComprehensiveStylesFinder { } } - ts.forEachChild(node, visit); + node.forEachChild(visit); }; visit(sourceFile); @@ -517,12 +528,12 @@ class ComprehensiveStylesFinder { } // CLI interface -function main() { +async function main() { const rootDir = process.cwd(); try { const finder = new ComprehensiveStylesFinder(rootDir); - const unusedStyles = finder.findUnusedStyles(); + const unusedStyles = await finder.findUnusedStyles(); if (unusedStyles.length === 0) { console.log('✅ No unused styles found!'); diff --git a/tests/unit/findUnusedStylesTest.ts b/tests/tooling/findUnusedStyles.test.ts similarity index 73% rename from tests/unit/findUnusedStylesTest.ts rename to tests/tooling/findUnusedStyles.test.ts index 063abce58a33..57a35d65134a 100644 --- a/tests/unit/findUnusedStylesTest.ts +++ b/tests/tooling/findUnusedStyles.test.ts @@ -1,30 +1,26 @@ +import {afterEach, beforeEach, describe, expect, it, jest, mock, test} from 'bun:test'; + import type {Stats} from 'fs'; -import type * as GlobModule from 'glob'; import {Str} from 'expensify-common'; import * as fs from 'fs'; -import {globSync} from 'glob'; +import * as glob from 'glob'; +import {API as CompilerAPI, RetainedSourceFile} from 'typescript/async'; -import {ComprehensiveStylesFinder} from '../../scripts/findUnusedStyles'; import createMock from '../utils/createMock'; -jest.mock('fs', () => ({ - ...jest.requireActual('fs'), - readFileSync: jest.fn(), - lstatSync: jest.fn(), -})); +const mockReadFileSync = jest.fn(); +const mockLstatSync = jest.fn(); +const mockGlobSync = jest.fn(); -jest.mock('glob', () => ({ - ...jest.requireActual('glob'), - globSync: jest.fn(), -})); +await mock.module('fs', () => ({...fs, readFileSync: mockReadFileSync, lstatSync: mockLstatSync})); +await mock.module('glob', () => ({...glob, globSync: mockGlobSync})); -const mockReadFileSync = jest.mocked(fs.readFileSync); -const mockLstatSync = jest.mocked(fs.lstatSync); -const mockGlobSync = jest.mocked(globSync); +// Bun imports the native compiler's ESM client directly, without Jest's CommonJS transform. +const {ComprehensiveStylesFinder} = await import('../../scripts/findUnusedStyles'); describe('findUnusedStyles', () => { - let finder: ComprehensiveStylesFinder; + let finder: InstanceType; beforeEach(() => { jest.clearAllMocks(); @@ -39,19 +35,51 @@ describe('findUnusedStyles', () => { jest.restoreAllMocks(); }); + it('releases native source files and closes the compiler after finding styles', async () => { + // Given a parsed style file, native allocations must not survive the scan. + const closeCompiler = jest.spyOn(CompilerAPI.prototype, Symbol.asyncDispose); + const releaseSourceFile = jest.spyOn(RetainedSourceFile.prototype, Symbol.asyncDispose); + mockGlobSync.mockReturnValueOnce(['src/styles/example.ts']).mockReturnValueOnce([]); + mockReadFileSync.mockReturnValueOnce('const unusedStyle = {color: "red"};'); + + // When the scan finishes, it still reports the unused definition. + const result = await new ComprehensiveStylesFinder('/test').findUnusedStyles(); + + // Then both the source lease and the compiler are disposed. + expect(result.map((style) => style.key)).toEqual(['unusedStyle']); + expect(releaseSourceFile).toHaveBeenCalledTimes(1); + expect(closeCompiler).toHaveBeenCalledTimes(1); + }); + + it('closes the compiler when a source file cannot be read', async () => { + // Given an unreadable style file, the scan must also release its compiler on the error path. + const closeCompiler = jest.spyOn(CompilerAPI.prototype, Symbol.asyncDispose); + mockGlobSync.mockReturnValueOnce(['src/styles/example.ts']).mockReturnValueOnce([]); + mockReadFileSync.mockImplementationOnce(() => { + throw new Error('File is unreadable'); + }); + + // When file loading fails, the existing warning behavior allows the scan to finish. + const result = await new ComprehensiveStylesFinder('/test').findUnusedStyles(); + + // Then no definitions are reported and the compiler is still disposed. + expect(result).toEqual([]); + expect(closeCompiler).toHaveBeenCalledTimes(1); + }); + describe('Basic Functionality', () => { - it('should find no unused styles when styles file is empty', () => { + it('should find no unused styles when styles file is empty', async () => { // Setup: Empty styles file, no source files mockGlobSync.mockReturnValueOnce([]); // No style files (first call in findAllStyleDefinitions) mockGlobSync.mockReturnValueOnce([]); // No source files (second call in loadAllFileContents) finder = new ComprehensiveStylesFinder('/test'); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toEqual([]); }); - it('should detect single unused style', () => { + it('should detect single unused style', async () => { // Setup: One style file with one style, no usage const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ @@ -70,14 +98,14 @@ describe('findUnusedStyles', () => { .mockReturnValueOnce('// No style usage here'); // component file finder = new ComprehensiveStylesFinder('/test'); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('unusedStyle'); expect(result.at(0)?.file).toBe('src/styles/index.ts'); }); - test('should not report used styles', () => { + test('should not report used styles', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ usedStyle: { @@ -98,14 +126,14 @@ describe('findUnusedStyles', () => { mockReadFileSync.mockReturnValueOnce(stylesContent).mockReturnValueOnce(componentContent); finder = new ComprehensiveStylesFinder('/test'); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toHaveLength(0); }); }); describe('Complex Usage Patterns', () => { - it('should detect destructured style usage', () => { + it('should detect destructured style usage', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ destructuredStyle: { color: 'green' }, @@ -121,13 +149,13 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('unusedStyle'); }); - it('should detect spread pattern usage', () => { + it('should detect spread pattern usage', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ baseStyle: { padding: 10 }, @@ -146,14 +174,14 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); // When extendedStyle is used, baseStyle should not be unused since it's spread into extendedStyle expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('unusedStyle'); }); - it('should detect object spread pattern usage', () => { + it('should detect object spread pattern usage', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ baseStyle: { p10: { padding: 10 } }, @@ -172,14 +200,14 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); // When extendedStyle is used, baseStyle should not be unused since it's spread into extendedStyle expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('unusedStyle'); }); - test('should handle dynamic style access', () => { + test('should handle dynamic style access', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ dynamicStyle: { color: 'purple' }, @@ -194,7 +222,7 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('unusedStyle'); @@ -202,7 +230,7 @@ describe('findUnusedStyles', () => { }); describe('Utils/Generators/Themes Integration', () => { - test('should detect styles used in utils files', () => { + test('should detect styles used in utils files', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ utilUsedStyle: { fontWeight: 'bold' }, @@ -220,7 +248,7 @@ describe('findUnusedStyles', () => { mockReadFileSync.mockReturnValueOnce(stylesContent).mockReturnValueOnce(utilContent); finder = new ComprehensiveStylesFinder('/test'); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('unusedStyle'); @@ -228,7 +256,7 @@ describe('findUnusedStyles', () => { }); describe('Edge Cases', () => { - test('should ignore styles mentioned in comments', () => { + test('should ignore styles mentioned in comments', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ commentedStyle: { color: 'orange' }, @@ -245,13 +273,13 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); expect(result).toHaveLength(1); expect(result.at(0)?.key).toBe('commentedStyle'); }); - test('should skip helper constants in styles/index.ts', () => { + test('should skip helper constants in styles/index.ts', async () => { const stylesContent = Str.dedent(` const touchCalloutNone = { WebkitTouchCallout: 'none' }; const lineHeightBadge = { lineHeight: 16 }; @@ -271,13 +299,13 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); // Should not include touchCalloutNone or lineHeightBadge expect(result).toHaveLength(0); }); - test('should handle malformed TypeScript files gracefully', () => { + test('should handle malformed TypeScript files gracefully', async () => { const stylesContent = Str.dedent(` const styles = (theme: ThemeColors) => ({ validStyle: { color: 'blue' }, @@ -293,16 +321,13 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, malformedContent); - // Should not throw an error - expect(() => { - const result = finder.findUnusedStyles(); - expect(result).toHaveLength(0); // Style is used despite syntax error - }).not.toThrow(); + // A parse error must not hide style usages in the recovered syntax tree. + await expect(finder.findUnusedStyles()).resolves.toHaveLength(0); }); }); describe('Error Handling', () => { - test('should handle file read errors gracefully', () => { + test('should handle file read errors gracefully', async () => { mockGlobSync.mockReturnValueOnce(['src/styles/index.ts']).mockReturnValueOnce(['src/components/Test.tsx']); mockReadFileSync @@ -313,10 +338,10 @@ describe('findUnusedStyles', () => { finder = new ComprehensiveStylesFinder('/test'); - expect(() => finder.findUnusedStyles()).not.toThrow(); + await expect(finder.findUnusedStyles()).resolves.toEqual([]); }); - test('should handle directory instead of file', () => { + test('should handle directory instead of file', async () => { mockGlobSync.mockReturnValueOnce(['src/styles/index.ts']).mockReturnValueOnce(['src/components/Test.tsx']); mockReadFileSync.mockReturnValueOnce('const styles = () => ({});'); @@ -330,12 +355,12 @@ describe('findUnusedStyles', () => { finder = new ComprehensiveStylesFinder('/test'); - expect(() => finder.findUnusedStyles()).not.toThrow(); + await expect(finder.findUnusedStyles()).resolves.toEqual([]); }); }); describe('Performance Tests', () => { - test('should handle large number of styles efficiently', () => { + test('should handle large number of styles efficiently', async () => { const manyStyles = Array.from({length: 1000}, (_, i) => `style${i}: { color: 'color${i}' },`).join('\n'); const stylesContent = Str.dedent(` @@ -353,7 +378,7 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, componentContent); const start = Date.now(); - const result = finder.findUnusedStyles(); + const result = await finder.findUnusedStyles(); const end = Date.now(); expect(result).toHaveLength(999); // All except style0 From 44207c1806de0f4051d8eba47765afffe5149bde Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 13:23:07 -0800 Subject: [PATCH 04/10] fix: await native scanner I/O before assertions Bun promise matchers can stall native compiler RPC callbacks. Await scans directly, preserving all assertions and the five-second performance threshold. The native scanner suite passes all 16 cases in three consecutive runs. Full App typecheck, changed-file lint/spelling, formatting, and real CLI smoke checks pass. Staged self-review used because nested reviewer agents are unavailable. --- tests/tooling/findUnusedStyles.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/tooling/findUnusedStyles.test.ts b/tests/tooling/findUnusedStyles.test.ts index 57a35d65134a..e6a2bbf0e379 100644 --- a/tests/tooling/findUnusedStyles.test.ts +++ b/tests/tooling/findUnusedStyles.test.ts @@ -19,6 +19,7 @@ await mock.module('glob', () => ({...glob, globSync: mockGlobSync})); // Bun imports the native compiler's ESM client directly, without Jest's CommonJS transform. const {ComprehensiveStylesFinder} = await import('../../scripts/findUnusedStyles'); +// Await compiler I/O before asserting because Bun's promise matchers can stall native RPC callbacks. describe('findUnusedStyles', () => { let finder: InstanceType; @@ -322,7 +323,8 @@ describe('findUnusedStyles', () => { setupMockFiles(stylesContent, malformedContent); // A parse error must not hide style usages in the recovered syntax tree. - await expect(finder.findUnusedStyles()).resolves.toHaveLength(0); + const result = await finder.findUnusedStyles(); + expect(result).toHaveLength(0); }); }); @@ -338,7 +340,8 @@ describe('findUnusedStyles', () => { finder = new ComprehensiveStylesFinder('/test'); - await expect(finder.findUnusedStyles()).resolves.toEqual([]); + const result = await finder.findUnusedStyles(); + expect(result).toEqual([]); }); test('should handle directory instead of file', async () => { @@ -355,7 +358,8 @@ describe('findUnusedStyles', () => { finder = new ComprehensiveStylesFinder('/test'); - await expect(finder.findUnusedStyles()).resolves.toEqual([]); + const result = await finder.findUnusedStyles(); + expect(result).toEqual([]); }); }); From 6a7555c44e2ddbbf52b3f80e8c4d647e9402a3f9 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 13:35:01 -0800 Subject: [PATCH 05/10] refactor: migrate translation tooling to TS7 API --- scripts/generateTranslations.ts | 207 +++--- scripts/utils/TSCompilerUtils.ts | 180 +++--- .../TSCompilerUtils.test.ts} | 610 +++++++++++------- tests/tooling/generateTranslations.test.ts | 15 +- 4 files changed, 594 insertions(+), 418 deletions(-) rename tests/{unit/TSCompilerUtilsTest.ts => tooling/TSCompilerUtils.test.ts} (72%) diff --git a/scripts/generateTranslations.ts b/scripts/generateTranslations.ts index 05b0e5984316..397ad395f73e 100755 --- a/scripts/generateTranslations.ts +++ b/scripts/generateTranslations.ts @@ -13,9 +13,9 @@ import type {TranslationTargetLocale} from '@src/CONST/LOCALES'; import en from '@src/languages/en'; import type {TranslationPaths} from '@src/languages/types'; -import type {TemplateExpression} from '@typescript/typescript6'; +import type {TemplateExpression} from 'typescript/ast'; +import type {RetainedSourceFile} from 'typescript/async'; -import ts from '@typescript/typescript6'; import {execFileSync} from 'child_process'; import * as dotenv from 'dotenv'; import {Str} from 'expensify-common'; @@ -24,11 +24,14 @@ import fs from 'fs'; // eslint-disable-next-line you-dont-need-lodash-underscore/get import get from 'lodash/get'; import path from 'path'; +import * as ts from 'typescript/ast'; +import * as factory from 'typescript/ast/factory'; +import {API} from 'typescript/async'; import type {DiffResult} from './utils/Git'; import type Translator from './utils/Translator/Translator'; import type {StringWithContext} from './utils/Translator/types'; -import type {TransformerResult} from './utils/TSCompilerUtils'; +import type {SourceFileTransformer, TransformerResult} from './utils/TSCompilerUtils'; import COLORS from './utils/COLORS'; import Git from './utils/Git'; @@ -52,8 +55,6 @@ const GENERATED_FILE_PREFIX = Str.dedent(` */ `); -const tsPrinter = ts.createPrinter({removeComments: true}); - /** * If the estimated cost of translation exceeds this threshold (in USD), prompt the user for confirmation before proceeding. */ @@ -106,7 +107,11 @@ class TranslationGenerator { /** * The file to use as the source of truth for translations. */ - private readonly sourceFile: ts.SourceFile; + private sourceFile!: ts.SourceFile; + + private readonly api: API; + + private readonly sourceFileLeases: RetainedSourceFile[] = []; /** * Translator module to perform translations. @@ -211,9 +216,9 @@ class TranslationGenerator { */ private readonly dedentStringKeys = new Set(); - constructor() { + constructor(api: API) { + this.api = api; this.languagesDir = process.env.LANGUAGES_DIR ?? path.join(__dirname, '../src/languages'); - const enSourceFile = path.join(this.languagesDir, 'en.ts'); /* eslint-disable @typescript-eslint/naming-convention */ this.cli = new CLI({ @@ -306,9 +311,6 @@ class TranslationGenerator { throw new Error('GITHUB_TOKEN environment variable is required when using --pr-number'); } - const sourceCode = fs.readFileSync(enSourceFile, 'utf8'); - this.sourceFile = ts.createSourceFile(enSourceFile, sourceCode, ts.ScriptTarget.Latest, true); - if (this.cli.flags['dry-run']) { console.log('🍸 Dry run enabled'); this.translator = new DummyTranslator(); @@ -324,34 +326,46 @@ class TranslationGenerator { } public async generateTranslations(): Promise { - // map of translations for each locale - const translations = new Map>(); + try { + const enSourceFile = path.join(this.languagesDir, 'en.ts'); + this.sourceFile = await this.parseSourceFile(enSourceFile, fs.readFileSync(enSourceFile, 'utf8')); + // map of translations for each locale + const translations = new Map>(); - if (this.isIncremental && this.pathsToModify.size === 0) { - await this.buildPathsFromGitDiff(); - } + if (this.isIncremental && this.pathsToModify.size === 0) { + await this.buildPathsFromGitDiff(); + } - if (this.verbose) { - console.log(`🎯 Initial path sets:`); - console.log(` pathsToModify: ${Array.from(this.pathsToModify).join(', ')}`); - console.log(` pathsToAdd: ${Array.from(this.pathsToAdd).join(', ')}`); - console.log(` pathsToRemove: ${Array.from(this.pathsToRemove).join(', ')}`); - } + if (this.verbose) { + console.log(`🎯 Initial path sets:`); + console.log(` pathsToModify: ${Array.from(this.pathsToModify).join(', ')}`); + console.log(` pathsToAdd: ${Array.from(this.pathsToAdd).join(', ')}`); + console.log(` pathsToRemove: ${Array.from(this.pathsToRemove).join(', ')}`); + } + + // Extract strings to translate once (locale-independent) + const stringsToTranslate = new Map(); + this.extractStringsToTranslate(this.sourceFile, stringsToTranslate); - // Extract strings to translate once (locale-independent) - const stringsToTranslate = new Map(); - this.extractStringsToTranslate(this.sourceFile, stringsToTranslate); + // Estimate cost and prompt user if needed (respects --yes/--no flags) + await this.promptForCostApproval(stringsToTranslate); - // Estimate cost and prompt user if needed (respects --yes/--no flags) - await this.promptForCostApproval(stringsToTranslate); + // Translate locales sequentially (each locale fully translates before the next begins) + for (const targetLanguage of this.targetLanguages) { + await this.generateTranslationsForLocale(targetLanguage, stringsToTranslate, translations.get(targetLanguage) ?? new Map()); + } - // Translate locales sequentially (each locale fully translates before the next begins) - for (const targetLanguage of this.targetLanguages) { - await this.generateTranslationsForLocale(targetLanguage, stringsToTranslate, translations.get(targetLanguage) ?? new Map()); + // Print error summary if there were any failures + this.printErrorSummary(); + } finally { + await Promise.all(this.sourceFileLeases.map((lease) => lease.dispose())); } + } - // Print error summary if there were any failures - this.printErrorSummary(); + private async parseSourceFile(fileName: string, sourceText: string): Promise { + const lease = await this.api.createSourceFile(fileName, sourceText); + this.sourceFileLeases.push(lease); + return lease.sourceFile; } /** @@ -452,41 +466,33 @@ class TranslationGenerator { // Transform en.ts with path filtering for pathsToModify and pathsToAdd. // The result is a "patch" of the main translations node, including only the paths that are added or modified, // where the values are translated to the target language. - const enResult = ts.transform(this.sourceFile, [this.createTranslationTransformer(translationsForLocale)]); - const transformedEnSourceFile = enResult.transformed.at(0); - if (!transformedEnSourceFile) { - throw new Error('Failed to create translated patch from en.ts'); - } + const transformedEnSourceFile = this.createTranslationTransformer(translationsForLocale)(this.sourceFile); // Extract translated code strings from the transformed en.ts const translatedCodeMap = new Map(); - this.extractTranslatedNodes(transformedEnSourceFile, translatedCodeMap); - enResult.dispose(); + await this.extractTranslatedNodes(transformedEnSourceFile, translatedCodeMap); + + const translatedNodes = new Map(); + for (const [translationPath, code] of translatedCodeMap) { + translatedNodes.set(translationPath, await TSCompilerUtils.parseCodeStringToAST(code, this.api)); + } // Transform the target file using the translated node map const existingContent = fs.readFileSync(targetPath, 'utf8'); - const existingSourceFile = ts.createSourceFile(targetPath, existingContent, ts.ScriptTarget.Latest, true); - const targetTransformer = this.createIncrementalTargetTransformer(translatedCodeMap); - const targetResult = ts.transform(existingSourceFile, [targetTransformer]); - const transformedTargetResult = targetResult.transformed.at(0); - if (!transformedTargetResult) { - throw new Error('Failed to transform target file'); - } - transformedSourceFile = transformedTargetResult; - targetResult.dispose(); + const existingSourceFile = await this.parseSourceFile(targetPath, existingContent); + const targetTransformer = this.createIncrementalTargetTransformer(translatedNodes); + transformedSourceFile = targetTransformer(existingSourceFile); } else { // Full transformation for non-incremental mode - transform en.ts const transformer = this.createTranslationTransformer(translationsForLocale); - const result = ts.transform(this.sourceFile, [transformer]); - transformedSourceFile = result.transformed.at(0) ?? this.sourceFile; - result.dispose(); + transformedSourceFile = transformer(this.sourceFile); } // Import en.ts (addImport will check if it already exists) transformedSourceFile = TSCompilerUtils.addImport(transformedSourceFile, 'en', './en', true); // Generate translated TypeScript code - const translatedCode = decodeUnicode(tsPrinter.printFile(transformedSourceFile)); + const translatedCode = decodeUnicode(await TSCompilerUtils.printWithoutComments(this.api, transformedSourceFile)); // Write to file const outputPath = path.join(this.languagesDir, `${targetLanguage}.ts`); @@ -503,7 +509,7 @@ class TranslationGenerator { Oxfmt.format(outputPath); // Apply dedent formatting after Oxfmt so we have accurate source positions - this.formatDedentCallsInFile(outputPath); + await this.formatDedentCallsInFile(outputPath); // Format again with Oxfmt to ensure consistent formatting after dedent transformation Oxfmt.format(outputPath); @@ -679,7 +685,7 @@ class TranslationGenerator { /** * Check if a call expression is a dedent() call (either standalone or Str.dedent()). */ - private isDedentCallExpression(expression: ts.LeftHandSideExpression): boolean { + private isDedentCallExpression(expression: ts.Expression): boolean { if (ts.isIdentifier(expression) && expression.text === 'dedent') { return true; } @@ -892,7 +898,7 @@ class TranslationGenerator { const matches = [...input.matchAll(regex)]; const headText = input.slice(0, matches.at(0)?.index ?? input.length); - const templateHead = ts.factory.createTemplateHead(headText); + const templateHead = factory.createTemplateHead(headText, '', ts.TokenFlags.None); const spans: ts.TemplateSpan[] = []; for (let i = 0; i < matches.length; i++) { @@ -921,7 +927,7 @@ class TranslationGenerator { expression = translatedExpression; } else { // Assume it's a simple identifier or property access - expression = ts.factory.createIdentifier(trimmed); + expression = factory.createIdentifier(trimmed); } const startOfMatch = match.index; @@ -929,12 +935,12 @@ class TranslationGenerator { const nextStaticTextEnd = i + 1 < matches.length ? matches.at(i + 1)?.index : input.length; const staticText = input.slice(nextStaticTextStart, nextStaticTextEnd); - const literal = i === matches.length - 1 ? ts.factory.createTemplateTail(staticText) : ts.factory.createTemplateMiddle(staticText); + const literal = i === matches.length - 1 ? factory.createTemplateTail(staticText, '', ts.TokenFlags.None) : factory.createTemplateMiddle(staticText, '', ts.TokenFlags.None); - spans.push(ts.factory.createTemplateSpan(expression, literal)); + spans.push(factory.createTemplateSpan(expression, literal)); } - return ts.factory.createTemplateExpression(templateHead, spans); + return factory.createTemplateExpression(templateHead, spans); } /** @@ -1068,7 +1074,7 @@ class TranslationGenerator { if (fs.existsSync(targetPath)) { const existingContent = fs.readFileSync(targetPath, 'utf8'); - const existingSourceFile = ts.createSourceFile(targetPath, existingContent, ts.ScriptTarget.Latest, true); + const existingSourceFile = await this.parseSourceFile(targetPath, existingContent); // Check each path in pathsToModify to see if it actually exists in this target file const existingTranslationsNode = this.findTranslationsNode(existingSourceFile); @@ -1172,7 +1178,7 @@ class TranslationGenerator { // String literals and no-substitution templates can be translated directly if (ts.isStringLiteral(node)) { const translatedText = translations.get(this.getTranslationKey(node)); - return translatedText ? ts.factory.createStringLiteral(translatedText) : nodeToUse; + return translatedText ? factory.createStringLiteral(translatedText, ts.TokenFlags.None) : nodeToUse; } if (ts.isNoSubstitutionTemplateLiteral(node)) { @@ -1181,7 +1187,7 @@ class TranslationGenerator { return nodeToUse; } - return ts.factory.createNoSubstitutionTemplateLiteral(translatedText); + return factory.createNoSubstitutionTemplateLiteral(translatedText, ts.TokenFlags.None); } if (ts.isTemplateExpression(node)) { @@ -1217,7 +1223,8 @@ class TranslationGenerator { /** * Extract translated code strings from a transformed AST for the specified paths. */ - private extractTranslatedNodes(sourceFile: ts.SourceFile, translatedCodeMap: Map): void { + private async extractTranslatedNodes(sourceFile: ts.SourceFile, translatedCodeMap: Map): Promise { + const nodesToPrint = new Map(); const visitWithPath = (node: ts.Node, currentPath = '') => { // Only extract code strings for exact paths in our sets (not hierarchical matches) const isAddedPath = this.pathsToAdd.has(currentPath as TranslationPaths); @@ -1229,8 +1236,7 @@ class TranslationGenerator { } // Extract the value (property initializer) as code string - const codeString = tsPrinter.printNode(ts.EmitHint.Expression, node.initializer, sourceFile); - translatedCodeMap.set(currentPath, codeString); + nodesToPrint.set(currentPath, node.initializer); return; // Stop recursing into children } @@ -1240,6 +1246,9 @@ class TranslationGenerator { }; visitWithPath(sourceFile); + for (const [translationPath, initializer] of nodesToPrint) { + translatedCodeMap.set(translationPath, await TSCompilerUtils.printWithoutComments(this.api, initializer)); + } } /** @@ -1256,7 +1265,7 @@ class TranslationGenerator { oldEnContent = Git.show(this.diffBase, relativePath); } - const oldSourceFile = ts.createSourceFile(this.sourceFile.fileName, oldEnContent, ts.ScriptTarget.Latest, true); + const oldSourceFile = await this.parseSourceFile(this.sourceFile.fileName, oldEnContent); const oldTranslationsNode = this.findTranslationsNode(oldSourceFile); // Traverse the old AST to find nodes on removed lines @@ -1272,7 +1281,7 @@ class TranslationGenerator { * Create a transformer factory for translating English code into another language. * For incremental translations, only translates paths that are in pathsToModify or pathsToAdd. */ - private createTranslationTransformer(translations: Map): ts.TransformerFactory { + private createTranslationTransformer(translations: Map): SourceFileTransformer { return TSCompilerUtils.createPathAwareTransformer((node: ts.Node, currentPath = ''): TransformerResult => { if (this.shouldTranslateNode(node) && this.shouldTranslatePath(currentPath)) { return { @@ -1288,19 +1297,16 @@ class TranslationGenerator { * Format all Str.dedent() calls in a file to ensure proper indentation. * This should be called after Oxfmt has formatted the file. */ - private formatDedentCallsInFile(filePath: string): void { + private async formatDedentCallsInFile(filePath: string): Promise { const fileContent = fs.readFileSync(filePath, 'utf8'); - const sourceFile = ts.createSourceFile(filePath, fileContent, ts.ScriptTarget.Latest, true); + const sourceFile = await this.parseSourceFile(filePath, fileContent); - const result = ts.transform(sourceFile, [this.createDedentFormattingTransformer()]); - const transformedFile = result.transformed.at(0); + const transformedFile = this.createDedentFormattingTransformer()(sourceFile); if (transformedFile) { - const formattedCode = decodeUnicode(tsPrinter.printFile(transformedFile)); + const formattedCode = decodeUnicode(await TSCompilerUtils.printWithoutComments(this.api, transformedFile)); fs.writeFileSync(filePath, formattedCode, 'utf8'); } - - result.dispose(); } /** @@ -1308,11 +1314,11 @@ class TranslationGenerator { * This runs after translation to ensure that template strings inside Str.dedent() calls * have the correct indentation structure. */ - private createDedentFormattingTransformer(): ts.TransformerFactory { - return (context: ts.TransformationContext) => (sourceFile: ts.SourceFile) => { + private createDedentFormattingTransformer(): SourceFileTransformer { + return (sourceFile: ts.SourceFile) => { const visit = (node: ts.Node): ts.Node => { if (!ts.isCallExpression(node) || !this.isDedentCallExpression(node.expression) || !node.arguments[0]) { - return ts.visitEachChild(node, visit, context); + return ts.visitEachChild(node, visit); } const argument = node.arguments[0]; @@ -1323,19 +1329,21 @@ class TranslationGenerator { const formatFragment = (text: string, isTail: boolean) => this.formatDedentTemplateContent(text, contentIndentation, templateIndentation, isTail); if (ts.isNoSubstitutionTemplateLiteral(argument)) { - return ts.factory.createCallExpression(node.expression, node.typeArguments, [ts.factory.createNoSubstitutionTemplateLiteral(formatFragment(argument.text, true))]); + return factory.updateCallExpression(node, node.expression, node.questionDotToken, node.typeArguments, [ + factory.createNoSubstitutionTemplateLiteral(formatFragment(argument.text, true), ts.TokenFlags.None), + ]); } if (ts.isTemplateExpression(argument)) { - const newHead = ts.factory.createTemplateHead(formatFragment(argument.head.text, false)); + const newHead = factory.createTemplateHead(formatFragment(argument.head.text, false), '', ts.TokenFlags.None); const newSpans = argument.templateSpans.map((span, i) => { const isTail = i === argument.templateSpans.length - 1; const formattedText = formatFragment(span.literal.text, isTail); - const newLiteral = isTail ? ts.factory.createTemplateTail(formattedText) : ts.factory.createTemplateMiddle(formattedText); - return ts.factory.createTemplateSpan(span.expression, newLiteral); + const newLiteral = isTail ? factory.createTemplateTail(formattedText, '', ts.TokenFlags.None) : factory.createTemplateMiddle(formattedText, '', ts.TokenFlags.None); + return factory.createTemplateSpan(span.expression, newLiteral); }); - return ts.factory.createCallExpression(node.expression, node.typeArguments, [ts.factory.createTemplateExpression(newHead, newSpans)]); + return factory.updateCallExpression(node, node.expression, node.questionDotToken, node.typeArguments, [factory.createTemplateExpression(newHead, newSpans)]); } return node; @@ -1410,11 +1418,11 @@ class TranslationGenerator { /** * Create a transformer factory for incremental translations of target files. - * Injects pathsToAdd and pathsToModify directly into the target file by parsing the code strings for the translated paths. + * Translated expressions are parsed before visiting because native parsing is asynchronous. * Removes pathsToRemove from the target file. * Also cleans up any empty object literals that result from the removals. */ - private createIncrementalTargetTransformer(translatedCodeMap: Map): ts.TransformerFactory { + private createIncrementalTargetTransformer(translatedNodes: Map): SourceFileTransformer { let mainTranslationsNode: ts.ObjectLiteralExpression | undefined; return TSCompilerUtils.createPathAwareTransformer((node: ts.Node, currentPath: string): TransformerResult => { if (!mainTranslationsNode) { @@ -1427,21 +1435,16 @@ class TranslationGenerator { } // Check if this is a property assignment that should be modified - if (ts.isPropertyAssignment(node) && currentPath && translatedCodeMap.has(currentPath)) { - const translatedCodeString = translatedCodeMap.get(currentPath); - if (!translatedCodeString) { + if (ts.isPropertyAssignment(node) && currentPath && translatedNodes.has(currentPath)) { + const translatedExpression = translatedNodes.get(currentPath); + if (!translatedExpression) { // This should never happen throw new Error('An unknown error occurred'); } - // Parse the code string back to an AST expression - const translatedExpression = TSCompilerUtils.parseCodeStringToAST(translatedCodeString); - - // Note: There is a minor bug here where leading comments (such as @context) are not preserved between the old property assignment node and the new one. - // This doesn't affect functionality at all, as translation target files don't need context annotations for anything, and it's not trivial to fix, so I'm leaving it as is for now. return { action: TransformerAction.Replace, - newNode: () => ts.factory.createPropertyAssignment(node.name, translatedExpression), + newNode: () => TSCompilerUtils.createPropertyAssignment(node.name, translatedExpression), }; } @@ -1478,12 +1481,9 @@ class TranslationGenerator { // Start with current properties let updatedProperties = [...properties]; - for (const [addPath, translatedCodeString] of translatedCodeMap) { - // Parse the translated code string back to an AST expression - const translatedExpression = TSCompilerUtils.parseCodeStringToAST(translatedCodeString); - + for (const [addPath, translatedExpression] of translatedNodes) { // Inject the value at the correct nested path - const currentObject = ts.factory.createObjectLiteralExpression(updatedProperties); + const currentObject = factory.createObjectLiteralExpression(updatedProperties); const updatedObject = TSCompilerUtils.injectDeepObjectValue(currentObject, addPath, translatedExpression); updatedProperties = [...updatedObject.properties]; hasChanges = true; @@ -1494,7 +1494,7 @@ class TranslationGenerator { } // Only create a new node if something actually changed - return hasChanges ? ts.factory.createObjectLiteralExpression(properties) : transformedNode; + return hasChanges ? factory.createObjectLiteralExpression(properties) : transformedNode; }, }; } @@ -1505,8 +1505,13 @@ class TranslationGenerator { } async function main(): Promise { - const generator = new TranslationGenerator(); - await generator.generateTranslations(); + const api = new API(); + try { + const generator = new TranslationGenerator(api); + await generator.generateTranslations(); + } finally { + await api.close(); + } } if (require.main === module) { diff --git a/scripts/utils/TSCompilerUtils.ts b/scripts/utils/TSCompilerUtils.ts index c2449220ec8a..e74f1d9c5ecf 100644 --- a/scripts/utils/TSCompilerUtils.ts +++ b/scripts/utils/TSCompilerUtils.ts @@ -1,4 +1,8 @@ -import ts from '@typescript/typescript6'; +import type {API} from 'typescript/async'; + +import * as ts from 'typescript/ast'; +import * as factory from 'typescript/ast/factory'; +import * as is from 'typescript/ast/is'; /** * Actions that can be taken when visiting a node in a transformer. @@ -16,6 +20,14 @@ enum TransformerAction { type TransformerResult = {action: TransformerAction.Continue} | {action: TransformerAction.Replace; newNode: (transformedChildNode: ts.Node) => ts.Node} | {action: TransformerAction.Remove}; +type SourceFileTransformer = (sourceFile: ts.SourceFile) => ts.SourceFile; + +function createPropertyAssignment(name: string | ts.PropertyName, initializer: ts.Expression): ts.PropertyAssignment { + // The beta declaration requires a type even for object properties without a type annotation. + // @ts-expect-error -- An absent property type is valid in the native factory and printer. + return factory.createPropertyAssignment(undefined, typeof name === 'string' ? factory.createIdentifier(name) : name, undefined, undefined, initializer); +} + /** * Walk up the AST from a given node and return the nearest ancestor that matches a predicate. * @@ -94,10 +106,10 @@ function addImport(sourceFile: ts.SourceFile, identifierName: string, modulePath } const phaseModifier = isTypeOnly ? ts.SyntaxKind.TypeKeyword : undefined; - const newImport = ts.factory.createImportDeclaration( + const newImport = factory.createImportDeclaration( undefined, - ts.factory.createImportClause(phaseModifier, ts.factory.createIdentifier(identifierName), undefined), - ts.factory.createStringLiteral(modulePath), + factory.createImportClause(phaseModifier, factory.createIdentifier(identifierName), undefined), + factory.createStringLiteral(modulePath, ts.TokenFlags.None), ); // Find the index of the last import declaration @@ -109,9 +121,9 @@ function addImport(sourceFile: ts.SourceFile, identifierName: string, modulePath } } - const updatedStatements = ts.factory.createNodeArray([...sourceFile.statements.slice(0, lastImportIndex + 1), newImport, ...sourceFile.statements.slice(lastImportIndex + 1)]); + const updatedStatements = factory.createNodeArray([...sourceFile.statements.slice(0, lastImportIndex + 1), newImport, ...sourceFile.statements.slice(lastImportIndex + 1)]); - return ts.factory.updateSourceFile(sourceFile, updatedStatements); + return factory.updateSourceFile(sourceFile, updatedStatements, sourceFile.endOfFileToken); } /** @@ -170,28 +182,6 @@ function resolveDeclaration(name: string, sourceFile: ts.SourceFile): ts.Node | return null; } -/** - * Check if a node is an expression that has both 'expression' and 'type' properties. - * This is useful for satisfies expressions and type assertions. - */ -function isExpressionWithType(node: ts.Node): node is ExpressionWithType { - return 'expression' in node && 'type' in node && node.expression !== undefined && node.type !== undefined; -} - -/** - * Check if a node is a satisfies expression by examining its structure. - * This is more robust than checking SyntaxKind numbers which might vary between TS versions. - */ -function isSatisfiesExpression(node: ts.Node): node is ExpressionWithType { - // Check if the node text contains 'satisfies' and has the expected structure - const nodeText = node.getText(); - if (!nodeText.includes(' satisfies ')) { - return false; - } - - return isExpressionWithType(node); -} - /** * Extracts the identifier name from various expression types. * Handles cases like: @@ -207,9 +197,7 @@ function extractIdentifierFromExpression(node: ts.Node): string | null { return node.text; } - // Check for satisfies expression by looking at the node structure - // A satisfies expression has the form: expression satisfies type - if (isSatisfiesExpression(node)) { + if (is.isSatisfiesExpression(node)) { return extractIdentifierFromExpression(node.expression); } @@ -223,11 +211,8 @@ function extractIdentifierFromExpression(node: ts.Node): string | null { return extractIdentifierFromExpression(node.expression); } - // Type assertion: `translations` - // Check for type assertion by looking for angle bracket syntax and structure - const nodeText = node.getText(); - if (nodeText.includes('<') && nodeText.includes('>') && 'expression' in node && 'type' in node && node.expression !== undefined && node.type !== undefined) { - return extractIdentifierFromExpression(node.expression as ts.Node); + if (is.isTypeAssertion(node)) { + return extractIdentifierFromExpression(node.expression); } return null; @@ -288,28 +273,28 @@ function buildDotNotationPath(node: ts.Node, rootNode?: ts.Node): string | null * Useful for converting serialized expressions back to AST nodes. * @disclaimer This is intended to work only for single expressions of code, not entire files or multiple statements. */ -function parseCodeStringToAST(codeString: string): ts.Expression { +async function parseCodeStringToAST(codeString: string, api: API): Promise { try { - const tempSourceFile = ts.createSourceFile('temp.ts', `const temp = ${codeString};`, ts.ScriptTarget.Latest); - - // Check for parsing errors - // @ts-expect-error parseDiagnostics is not a public property of the SourceFile type, but this works. - // The "correct" way to do this is with `ts.createProgram`, but it's more complicated and it makes the translation script at least ~4x slower. - if (tempSourceFile.parseDiagnostics && (tempSourceFile.parseDiagnostics as unknown as ts.Diagnostic[]).length > 0) { + const sourceText = `const temp = ${codeString};`; + const {diagnostics} = await api.transpileModule(sourceText, {fileName: 'temp.ts', reportDiagnostics: true}); + if (diagnostics?.length) { throw new Error(`Malformed code string: ${codeString}`); } - - const tempStatement = tempSourceFile.statements.at(0); - if (!tempStatement || !ts.isVariableStatement(tempStatement)) { - throw new Error(`Malformed code string: ${codeString}`); - } - - const declaration = tempStatement.declarationList.declarations.at(0); - if (!declaration?.initializer) { - throw new Error(`No initializer found in code string: ${codeString}`); + const lease = await api.createSourceFile('temp.ts', sourceText); + try { + const tempStatement = lease.sourceFile.statements.at(0); + if (!tempStatement || !ts.isVariableStatement(tempStatement)) { + throw new Error(`Malformed code string: ${codeString}`); + } + const declaration = tempStatement.declarationList.declarations.at(0); + if (!declaration?.initializer) { + throw new Error(`No initializer found in code string: ${codeString}`); + } + // A synthesized clone remains printable after the remote source file lease is released. + return ts.getSynthesizedDeepClone(declaration.initializer); + } finally { + await lease.dispose(); } - - return declaration.initializer; } catch (error) { if (error instanceof Error && error.message.includes('code string')) { throw error; // Re-throw our custom errors @@ -341,26 +326,24 @@ function createPathAwareVisitor(visitWithPath: (node: ts.Node, path: string) * Create a path-aware transformer that provides clear action-based control over node transformation. * Makes transformer logic more explicit and easier to understand. */ -function createPathAwareTransformer(visitor: (node: ts.Node, path: string) => TransformerResult): ts.TransformerFactory { - return (context: ts.TransformationContext) => { - const visitWithPath = (node: ts.Node, currentPath = ''): ts.Node | undefined => { - const result = visitor(node, currentPath); - if (result.action === TransformerAction.Remove) { - return undefined; - } +function createPathAwareTransformer(visitor: (node: ts.Node, path: string) => TransformerResult): SourceFileTransformer { + const visitWithPath = (node: ts.Node, currentPath = ''): ts.Node | undefined => { + const result = visitor(node, currentPath); + if (result.action === TransformerAction.Remove) { + return undefined; + } - const transformedNode = ts.visitEachChild(node, createPathAwareVisitor(visitWithPath, currentPath), context); - if (result.action === TransformerAction.Replace) { - return result.newNode(transformedNode); - } + const transformedNode = ts.visitEachChild(node, createPathAwareVisitor(visitWithPath, currentPath)); + if (result.action === TransformerAction.Replace) { + return result.newNode(transformedNode); + } - // Continue, leaving the existing node unchanged - return transformedNode; - }; + // Continue, leaving the existing node unchanged + return transformedNode; + }; - return (sourceFile: ts.SourceFile) => { - return (ts.visitNode(sourceFile, visitWithPath) as ts.SourceFile) ?? sourceFile; - }; + return (sourceFile: ts.SourceFile) => { + return (ts.visitNode(sourceFile, visitWithPath) as ts.SourceFile) ?? sourceFile; }; } @@ -449,9 +432,9 @@ function injectDeepObjectValue(objectLiteral: ts.ObjectLiteralExpression, dotPat // Key exists - we need to recursively inject into the existing structure if (!remainingPath) { // This is the final key, replace the existing value - const updatedProperty = ts.factory.createPropertyAssignment(topLevelKey, value); + const updatedProperty = createPropertyAssignment(topLevelKey, value); const updatedProperties = objectLiteral.properties.map((prop) => (prop === existingProperty ? updatedProperty : prop)); - return ts.factory.createObjectLiteralExpression(updatedProperties); + return factory.createObjectLiteralExpression(updatedProperties); } // Recursively inject into the existing nested structure @@ -473,27 +456,27 @@ function injectDeepObjectValue(objectLiteral: ts.ObjectLiteralExpression, dotPat const updatedNestedObject = injectDeepObjectValue(nestedObject, remainingPath, value); // Re-wrap with satisfies if it was originally wrapped - const finalValue = satisfiesType ? ts.factory.createSatisfiesExpression(updatedNestedObject, satisfiesType) : updatedNestedObject; + const finalValue = satisfiesType ? factory.createSatisfiesExpression(updatedNestedObject, satisfiesType) : updatedNestedObject; - const updatedProperty = ts.factory.createPropertyAssignment(topLevelKey, finalValue); + const updatedProperty = createPropertyAssignment(topLevelKey, finalValue); const updatedProperties = objectLiteral.properties.map((prop) => (prop === existingProperty ? updatedProperty : prop)); - return ts.factory.createObjectLiteralExpression(updatedProperties); + return factory.createObjectLiteralExpression(updatedProperties); } // Key doesn't exist - create new nested structure if (!remainingPath) { // This is a direct property, just add it - const newProperty = ts.factory.createPropertyAssignment(topLevelKey, value); - return ts.factory.createObjectLiteralExpression([...objectLiteral.properties, newProperty]); + const newProperty = createPropertyAssignment(topLevelKey, value); + return factory.createObjectLiteralExpression([...objectLiteral.properties, newProperty]); } // Create nested structure recursively by creating an empty object and injecting into it - const emptyNestedObject = ts.factory.createObjectLiteralExpression([]); + const emptyNestedObject = factory.createObjectLiteralExpression([]); const nestedObjectWithValue = injectDeepObjectValue(emptyNestedObject, remainingPath, value); - const newProperty = ts.factory.createPropertyAssignment(topLevelKey, nestedObjectWithValue); + const newProperty = createPropertyAssignment(topLevelKey, nestedObjectWithValue); // Return new ObjectLiteralExpression with the added property - return ts.factory.createObjectLiteralExpression([...objectLiteral.properties, newProperty]); + return factory.createObjectLiteralExpression([...objectLiteral.properties, newProperty]); } /** @@ -528,7 +511,39 @@ function isStringConcatenationChain(node: ts.BinaryExpression): boolean { return false; } +/** + * The native printer preserves comments, but translation targets must not carry English context annotations. + * Inspect parsed token trivia so comment markers inside strings, templates and regular expressions remain intact. + */ +async function printWithoutComments(api: API, node: ts.Node): Promise { + const code = await api.printer.printNode(node, {preserveSourceNewlines: false}); + const lease = await api.createSourceFile('printed.ts', code); + try { + const ranges = new Map(); + const collectComments = (child: ts.Node): void => { + for (const position of [child.pos, child.end]) { + const comments = [...(ts.getLeadingCommentRanges(code, position) ?? []), ...(ts.getTrailingCommentRanges(code, position) ?? [])]; + for (const comment of comments) { + ranges.set(comment.pos, comment.end); + } + } + for (const token of child.getChildren(lease.sourceFile)) { + collectComments(token); + } + }; + collectComments(lease.sourceFile); + let result = code; + for (const [start, end] of [...ranges].sort(([left], [right]) => right - left)) { + result = result.slice(0, start) + result.slice(start, end).replaceAll(/[^\r\n]/g, ' ') + result.slice(end); + } + return result; + } finally { + await lease.dispose(); + } +} + export default { + createPropertyAssignment, findAncestor, getIndentationOfNode, addImport, @@ -543,6 +558,7 @@ export default { objectHas, injectDeepObjectValue, isStringConcatenationChain, + printWithoutComments, }; export {TransformerAction}; -export type {ExpressionWithType, TransformerResult}; +export type {ExpressionWithType, SourceFileTransformer, TransformerResult}; diff --git a/tests/unit/TSCompilerUtilsTest.ts b/tests/tooling/TSCompilerUtils.test.ts similarity index 72% rename from tests/unit/TSCompilerUtilsTest.ts rename to tests/tooling/TSCompilerUtils.test.ts index 859eb140f282..87ca5ae3f85c 100644 --- a/tests/unit/TSCompilerUtilsTest.ts +++ b/tests/tooling/TSCompilerUtils.test.ts @@ -1,14 +1,37 @@ -import ts from '@typescript/typescript6'; +import {afterEach, beforeEach, describe, expect, it} from 'bun:test'; + +import type {RetainedSourceFile} from 'typescript/async'; + import {Str} from 'expensify-common'; +import * as ts from 'typescript/ast'; +import * as factory from 'typescript/ast/factory'; +import {API} from 'typescript/async'; + +import TSCompilerUtils, {TransformerAction} from '../../scripts/utils/TSCompilerUtils'; -import TSCompilerUtils from '../../scripts/utils/TSCompilerUtils'; +let api: API; +const leases: RetainedSourceFile[] = []; + +beforeEach(() => { + api = new API(); +}); -function createSourceFile(content: string): ts.SourceFile { - return ts.createSourceFile('test.ts', content, ts.ScriptTarget.Latest, true); +afterEach(async () => { + try { + await Promise.all(leases.splice(0).map((lease) => lease.dispose())); + } finally { + await api.close(); + } +}); + +async function createSourceFile(content: string): Promise { + const lease = await api.createSourceFile('test.ts', content); + leases.push(lease); + return lease.sourceFile; } -function printSourceFile(sourceFile: ts.SourceFile): string { - return ts.createPrinter().printFile(sourceFile); +function printSourceFile(sourceFile: ts.SourceFile): Promise { + return api.printer.printFile(sourceFile); } function expectNode(node: ts.Node | undefined, guard: (candidate: ts.Node) => candidate is T, expected: string): T { @@ -67,15 +90,15 @@ function expectStringLiteralText(node: ts.Expression, expected: string): void { describe('TSCompilerUtils', () => { describe('addImport', () => { - it('adds a default import after an existing import', () => { - const source = createSourceFile( + it('adds a default import after an existing import', async () => { + const source = await createSourceFile( Str.dedent(` import fs from 'fs'; console.log('hello'); `), ); const updated = TSCompilerUtils.addImport(source, 'myModule', 'some-path'); - const output = printSourceFile(updated); + const output = await printSourceFile(updated); expect(output).toBe( Str.dedent(` import fs from 'fs'; @@ -85,14 +108,14 @@ describe('TSCompilerUtils', () => { ); }); - it('adds a default import at the top when there are no imports', () => { - const source = createSourceFile( + it('adds a default import at the top when there are no imports', async () => { + const source = await createSourceFile( Str.dedent(` console.log('hello'); `), ); const updated = TSCompilerUtils.addImport(source, 'myModule', 'some-path'); - const output = printSourceFile(updated); + const output = await printSourceFile(updated); expect(output).toBe( Str.dedent(` import myModule from "some-path"; @@ -101,8 +124,8 @@ describe('TSCompilerUtils', () => { ); }); - it('adds after multiple imports', () => { - const source = createSourceFile( + it('adds after multiple imports', async () => { + const source = await createSourceFile( Str.dedent(` import fs from 'fs'; import path from 'path'; @@ -113,7 +136,7 @@ describe('TSCompilerUtils', () => { `), ); const updated = TSCompilerUtils.addImport(source, 'myModule', 'some-path'); - const output = printSourceFile(updated); + const output = await printSourceFile(updated); expect(output).toBe( Str.dedent(` import fs from 'fs'; @@ -126,10 +149,10 @@ describe('TSCompilerUtils', () => { ); }); - it('adds to an empty file', () => { - const source = createSourceFile(``); + it('adds to an empty file', async () => { + const source = await createSourceFile(``); const updated = TSCompilerUtils.addImport(source, 'init', './init'); - const output = printSourceFile(updated); + const output = await printSourceFile(updated); expect(output).toBe( Str.dedent(` @@ -138,15 +161,15 @@ describe('TSCompilerUtils', () => { ); }); - it('supports type-only imports', () => { - const source = createSourceFile( + it('supports type-only imports', async () => { + const source = await createSourceFile( Str.dedent(` import fs from 'fs'; console.log('hello'); `), ); const updated = TSCompilerUtils.addImport(source, 'MyType', 'some-path', true); - const output = printSourceFile(updated); + const output = await printSourceFile(updated); expect(output).toBe( Str.dedent(` import fs from 'fs'; @@ -158,21 +181,21 @@ describe('TSCompilerUtils', () => { }); describe('findDefaultExport', () => { - it('returns the identifier in `export default` statement', () => { + it('returns the identifier in `export default` statement', async () => { const code = Str.dedent(` const strings = { greeting: 'Hello' }; export default strings; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const result = TSCompilerUtils.findDefaultExport(ast); expect(result?.getText()).toBe('strings'); }); - it('returns the object literal if directly exported', () => { + it('returns the object literal if directly exported', async () => { const code = Str.dedent(` export default { farewell: 'Goodbye' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const result = TSCompilerUtils.findDefaultExport(ast); expect(result).not.toBeNull(); if (!result) { @@ -182,33 +205,33 @@ describe('TSCompilerUtils', () => { expect(result?.getText()).toContain('farewell'); }); - it('returns null if no default export is present', () => { + it('returns null if no default export is present', async () => { const code = Str.dedent(` const foo = 'bar'; export const greeting = 'Hello'; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const result = TSCompilerUtils.findDefaultExport(ast); expect(result).toBeNull(); }); - it('returns identifier for `export { foo as default }`', () => { + it('returns identifier for `export { foo as default }`', async () => { const code = Str.dedent(` const foo = { bar: 'baz' }; export { foo as default }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const result = TSCompilerUtils.findDefaultExport(ast); expect(result?.getText()).toBe('default'); }); }); describe('resolveDeclaration', () => { - it('resolves a variable declaration', () => { + it('resolves a variable declaration', async () => { const code = Str.dedent(` const foo = { message: 'hi' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const node = TSCompilerUtils.resolveDeclaration('foo', ast); expect(node).not.toBeNull(); @@ -219,13 +242,13 @@ describe('TSCompilerUtils', () => { expect(node.getText()).toContain('message'); }); - it('resolves a function declaration', () => { + it('resolves a function declaration', async () => { const code = Str.dedent(` function greet() { return 'hello'; } `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const node = TSCompilerUtils.resolveDeclaration('greet', ast); expect(node).not.toBeNull(); @@ -236,13 +259,13 @@ describe('TSCompilerUtils', () => { expect(node.getText()).toContain('hello'); }); - it('resolves a class declaration', () => { + it('resolves a class declaration', async () => { const code = Str.dedent(` class MyClass { method() {} } `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const node = TSCompilerUtils.resolveDeclaration('MyClass', ast); expect(node).not.toBeNull(); @@ -253,20 +276,20 @@ describe('TSCompilerUtils', () => { expect(node.getText()).toContain('method'); }); - it('returns null for unknown identifier', () => { + it('returns null for unknown identifier', async () => { const code = Str.dedent(` const foo = 123; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const node = TSCompilerUtils.resolveDeclaration('bar', ast); expect(node).toBeNull(); }); - it('returns declaration even if variable has no initializer', () => { + it('returns declaration even if variable has no initializer', async () => { const code = Str.dedent(` let foo; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const node = TSCompilerUtils.resolveDeclaration('foo', ast); expect(node).not.toBeNull(); @@ -278,106 +301,105 @@ describe('TSCompilerUtils', () => { }); describe('extractIdentifierFromExpression', () => { - it('extracts identifier from simple identifier', () => { + it('extracts identifier from simple identifier', async () => { const code = 'translations'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('extracts identifier from satisfies expression', () => { + it('extracts identifier from satisfies expression', async () => { const code = 'translations satisfies TranslationDeepObject;'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('extracts identifier from as expression', () => { + it('extracts identifier from as expression', async () => { const code = 'translations as SomeType;'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('extracts identifier from parenthesized expression', () => { + it('extracts identifier from parenthesized expression', async () => { const code = '(translations);'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('extracts identifier from nested parenthesized expression', () => { + it('extracts identifier from nested parenthesized expression', async () => { const code = '((translations));'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('extracts identifier from type assertion (angle bracket syntax)', () => { + it('extracts identifier from type assertion (angle bracket syntax)', async () => { const code = 'translations;'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); - // Note: This might be 'translations' or null depending on how TypeScript parses angle bracket syntax in JSX-enabled contexts - expect(result).toEqual(expect.any(String)); + expect(result).toBe('translations'); }); - it('extracts identifier from complex nested expression', () => { + it('extracts identifier from complex nested expression', async () => { const code = '(translations as SomeType);'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('extracts identifier from satisfies expression with nested parentheses', () => { + it('extracts identifier from satisfies expression with nested parentheses', async () => { const code = '(translations) satisfies TranslationDeepObject;'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); }); - it('returns null for non-identifier expressions', () => { + it('returns null for non-identifier expressions', async () => { const code = '"hello world";'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBeNull(); }); - it('returns null for complex expressions that do not contain identifiers', () => { + it('returns null for complex expressions that do not contain identifiers', async () => { const code = '42 + 24;'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBeNull(); }); - it('returns null for call expressions', () => { + it('returns null for call expressions', async () => { const code = 'someFunction();'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBeNull(); }); - it('returns null for member expressions', () => { + it('returns null for member expressions', async () => { const code = 'obj.property;'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBeNull(); }); - it('handles deeply nested expression types', () => { + it('handles deeply nested expression types', async () => { const code = '((translations as SomeType) satisfies AnotherType);'; - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const expression = getStatement(ast, 0, ts.isExpressionStatement, 'expression statement'); const result = TSCompilerUtils.extractIdentifierFromExpression(expression.expression); expect(result).toBe('translations'); @@ -385,13 +407,13 @@ describe('TSCompilerUtils', () => { }); describe('extractKeyFromPropertyNode', () => { - it('extracts key from property assignment with identifier', () => { + it('extracts key from property assignment with identifier', async () => { const code = Str.dedent(` const obj = { myKey: 'value' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const propertyAssignment = getProperty(objLiteral, 0, ts.isPropertyAssignment, 'property assignment'); @@ -400,13 +422,13 @@ describe('TSCompilerUtils', () => { expect(result).toBe('myKey'); }); - it('extracts key from property assignment with string literal', () => { + it('extracts key from property assignment with string literal', async () => { const code = Str.dedent(` const obj = { "myStringKey": 'value' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const propertyAssignment = getProperty(objLiteral, 0, ts.isPropertyAssignment, 'property assignment'); @@ -415,7 +437,7 @@ describe('TSCompilerUtils', () => { expect(result).toBe('myStringKey'); }); - it('extracts key from method declaration', () => { + it('extracts key from method declaration', async () => { const code = Str.dedent(` const obj = { myMethod() { @@ -423,7 +445,7 @@ describe('TSCompilerUtils', () => { } }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const methodDeclaration = getProperty(objLiteral, 0, ts.isMethodDeclaration, 'method declaration'); @@ -432,13 +454,13 @@ describe('TSCompilerUtils', () => { expect(result).toBe('myMethod'); }); - it('handles computed property names by returning undefined', () => { + it('handles computed property names by returning undefined', async () => { const code = Str.dedent(` const obj = { [computedKey]: 'value' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const propertyAssignment = getProperty(objLiteral, 0, ts.isPropertyAssignment, 'property assignment'); @@ -447,13 +469,13 @@ describe('TSCompilerUtils', () => { expect(result).toBeUndefined(); }); - it('handles numeric literal property names by returning undefined', () => { + it('handles numeric literal property names by returning undefined', async () => { const code = Str.dedent(` const obj = { 123: 'value' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const propertyAssignment = getProperty(objLiteral, 0, ts.isPropertyAssignment, 'property assignment'); @@ -462,7 +484,7 @@ describe('TSCompilerUtils', () => { expect(result).toBeUndefined(); }); - it('handles method declaration with complex name by returning undefined', () => { + it('handles method declaration with complex name by returning undefined', async () => { const code = Str.dedent(` const obj = { [Symbol.iterator]() { @@ -470,7 +492,7 @@ describe('TSCompilerUtils', () => { } }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const methodDeclaration = getProperty(objLiteral, 0, ts.isMethodDeclaration, 'method declaration'); @@ -479,13 +501,13 @@ describe('TSCompilerUtils', () => { expect(result).toBeUndefined(); }); - it('handles arrow function property assignment', () => { + it('handles arrow function property assignment', async () => { const code = Str.dedent(` const obj = { arrowFunc: () => 'hello' }; `); - const ast = createSourceFile(code); + const ast = await createSourceFile(code); const varDecl = getStatement(ast, 0, ts.isVariableStatement, 'variable statement'); const objLiteral = expectNode(varDecl.declarationList.declarations.at(0)?.initializer, ts.isObjectLiteralExpression, 'object literal initializer'); const propertyAssignment = getProperty(objLiteral, 0, ts.isPropertyAssignment, 'property assignment'); @@ -496,7 +518,7 @@ describe('TSCompilerUtils', () => { }); describe('addImport', () => { - it('adds import when it does not exist', () => { + it('adds import when it does not exist', async () => { const sourceCode = Str.dedent(` const strings = { greeting: 'Hello' @@ -504,15 +526,15 @@ describe('TSCompilerUtils', () => { export default strings; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const result = TSCompilerUtils.addImport(sourceFile, 'en', './en', true); - const resultCode = ts.createPrinter().printFile(result); + const resultCode = await printSourceFile(result); expect(resultCode).toContain('import type en from "./en";'); expect(resultCode).toContain('const strings = {'); }); - it('does not add duplicate import when it already exists', () => { + it('does not add duplicate import when it already exists', async () => { const sourceCode = Str.dedent(` import type en from './en'; const strings = { @@ -521,16 +543,16 @@ describe('TSCompilerUtils', () => { export default strings; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const result = TSCompilerUtils.addImport(sourceFile, 'en', './en', true); - const resultCode = ts.createPrinter().printFile(result); + const resultCode = await printSourceFile(result); // Should not have duplicate imports const importMatches = resultCode.match(/import type en from/g); expect(importMatches).toHaveLength(1); }); - it('distinguishes between type and value imports', () => { + it('distinguishes between type and value imports', async () => { const sourceCode = Str.dedent(` import en from './en'; const strings = { @@ -539,16 +561,16 @@ describe('TSCompilerUtils', () => { export default strings; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const result = TSCompilerUtils.addImport(sourceFile, 'en', './en', true); - const resultCode = ts.createPrinter().printFile(result); + const resultCode = await printSourceFile(result); // Should add type import even though value import exists expect(resultCode).toContain("import en from './en';"); expect(resultCode).toContain('import type en from "./en";'); }); - it('handles different module paths', () => { + it('handles different module paths', async () => { const sourceCode = Str.dedent(` import type en from './other'; const strings = { @@ -557,9 +579,9 @@ describe('TSCompilerUtils', () => { export default strings; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const result = TSCompilerUtils.addImport(sourceFile, 'en', './en', true); - const resultCode = ts.createPrinter().printFile(result); + const resultCode = await printSourceFile(result); // Should add new import with different path expect(resultCode).toContain("import type en from './other';"); @@ -568,7 +590,7 @@ describe('TSCompilerUtils', () => { }); describe('buildDotNotationPath', () => { - it('builds path from nested property assignment', () => { + it('builds path from nested property assignment', async () => { const sourceCode = Str.dedent(` const strings = { common: { @@ -577,7 +599,7 @@ describe('TSCompilerUtils', () => { }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableDeclaration = sourceFile.statements[0]; if (!ts.isVariableStatement(variableDeclaration)) { throw new Error('Expected variable statement'); @@ -612,14 +634,14 @@ describe('TSCompilerUtils', () => { expect(result).toBe('common.save'); }); - it('builds path from top-level property', () => { + it('builds path from top-level property', async () => { const sourceCode = Str.dedent(` const strings = { greeting: 'Hello' }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableDeclaration = sourceFile.statements[0]; if (!ts.isVariableStatement(variableDeclaration)) { throw new Error('Expected variable statement'); @@ -644,7 +666,7 @@ describe('TSCompilerUtils', () => { expect(result).toBe('greeting'); }); - it('builds path with custom root node', () => { + it('builds path with custom root node', async () => { const sourceCode = Str.dedent(` const strings = { common: { @@ -653,7 +675,7 @@ describe('TSCompilerUtils', () => { }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableDeclaration = sourceFile.statements[0]; if (!ts.isVariableStatement(variableDeclaration)) { throw new Error('Expected variable statement'); @@ -689,12 +711,12 @@ describe('TSCompilerUtils', () => { expect(result).toBe('save'); }); - it('returns null for nodes without property assignments', () => { + it('returns null for nodes without property assignments', async () => { const sourceCode = Str.dedent(` const greeting = 'Hello'; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableDeclaration = sourceFile.statements[0]; if (!ts.isVariableStatement(variableDeclaration)) { throw new Error('Expected variable statement'); @@ -709,7 +731,7 @@ describe('TSCompilerUtils', () => { expect(result).toBeNull(); }); - it('handles deeply nested paths', () => { + it('handles deeply nested paths', async () => { const sourceCode = Str.dedent(` const strings = { level1: { @@ -722,7 +744,7 @@ describe('TSCompilerUtils', () => { }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); // Navigate to the deeply nested string literal with proper type checking const variableDeclaration = sourceFile.statements[0]; if (!ts.isVariableStatement(variableDeclaration)) { @@ -778,8 +800,8 @@ describe('TSCompilerUtils', () => { expect(result).toBe('level1.level2.level3.deep'); }); - it('collapses paths inside a function-valued property to that property', () => { - const sourceFile = createSourceFile( + it('collapses paths inside a function-valued property to that property', async () => { + const sourceFile = await createSourceFile( Str.dedent(` const strings = { workspace: { @@ -810,46 +832,48 @@ describe('TSCompilerUtils', () => { }); describe('parseCodeStringToExpression', () => { - it('parses simple string literal', () => { - const result = TSCompilerUtils.parseCodeStringToAST('"Hello World"'); + it('parses simple string literal', async () => { + const result = await TSCompilerUtils.parseCodeStringToAST('"Hello World"', api); expect(ts.isStringLiteral(result)).toBe(true); if (ts.isStringLiteral(result)) { expect(result.text).toBe('Hello World'); } }); - it('parses template literal', () => { + it('parses template literal', async () => { // eslint-disable-next-line no-template-curly-in-string - const result = TSCompilerUtils.parseCodeStringToAST('`Hello ${name}`'); + const result = await TSCompilerUtils.parseCodeStringToAST('`Hello ${name}`', api); expect(ts.isTemplateExpression(result)).toBe(true); }); - it('parses complex expression', () => { - const result = TSCompilerUtils.parseCodeStringToAST('user.name ?? "Unknown"'); + it('parses complex expression', async () => { + const result = await TSCompilerUtils.parseCodeStringToAST('user.name ?? "Unknown"', api); expect(ts.isBinaryExpression(result)).toBe(true); }); - it('parses arrow function', () => { + it('parses arrow function', async () => { + // Given a function containing a template, its returned AST must outlive the parser's source file lease. + // The placeholder is fixture source, not an interpolation in this test. // eslint-disable-next-line no-template-curly-in-string - const result = TSCompilerUtils.parseCodeStringToAST('(name: string) => `Hello ${name}`'); + const code = '(name: string) => `Hello ${name}`'; + // When parsing completes, the helper has disposed its retained source file. + const result = await TSCompilerUtils.parseCodeStringToAST(code, api); + // Then the detached function retains its type annotation and template when printed remotely. expect(ts.isArrowFunction(result)).toBe(true); + expect(await api.printer.printNode(result)).toBe(code); }); - it('throws error for malformed code string', () => { - expect(() => { - TSCompilerUtils.parseCodeStringToAST('invalid syntax {'); - }).toThrow('Malformed code string'); + it('throws error for malformed code string', async () => { + await expect(TSCompilerUtils.parseCodeStringToAST('invalid syntax {', api)).rejects.toThrow('Malformed code string'); }); - it('throws error for empty code string', () => { - expect(() => { - TSCompilerUtils.parseCodeStringToAST('/* just a comment */'); - }).toThrow('Malformed code string'); + it('throws error for empty code string', async () => { + await expect(TSCompilerUtils.parseCodeStringToAST('/* just a comment */', api)).rejects.toThrow('Malformed code string'); }); }); describe('createPathAwareVisitor', () => { - it('should create visitor that builds correct paths for property assignments', () => { + it('should create visitor that builds correct paths for property assignments', async () => { const sourceCode = Str.dedent(` const strings = { greeting: 'Hello', @@ -859,7 +883,7 @@ describe('TSCompilerUtils', () => { }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableStatement = sourceFile.statements[0]; if (!ts.isVariableStatement(variableStatement)) { throw new Error('Expected variable statement'); @@ -878,16 +902,13 @@ describe('TSCompilerUtils', () => { return node; }, ''); - // Use the visitor with ts.visitEachChild - // @ts-expect-error nullTransformationContext exists but isn't a public API - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - ts.visitEachChild(objectLiteral, visitor, ts.nullTransformationContext); + ts.visitEachChild(objectLiteral, visitor); expect(visitedPaths).toContain('greeting'); expect(visitedPaths).toContain('common'); }); - it('should handle nested paths correctly', () => { + it('should handle nested paths correctly', async () => { const sourceCode = Str.dedent(` const strings = { common: { @@ -897,7 +918,7 @@ describe('TSCompilerUtils', () => { }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableStatement = sourceFile.statements[0]; if (!ts.isVariableStatement(variableStatement)) { throw new Error('Expected variable statement'); @@ -926,15 +947,13 @@ describe('TSCompilerUtils', () => { return node; }, 'common'); - // @ts-expect-error nullTransformationContext exists but isn't a public API - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - ts.visitEachChild(commonObject, visitor, ts.nullTransformationContext); + ts.visitEachChild(commonObject, visitor); expect(visitedPaths).toContain('common.save'); expect(visitedPaths).toContain('common.cancel'); }); - it('should work with forEachChild for non-transforming traversal', () => { + it('should work with forEachChild for non-transforming traversal', async () => { const sourceCode = Str.dedent(` const strings = { greeting: 'Hello', @@ -942,7 +961,7 @@ describe('TSCompilerUtils', () => { }; `); - const sourceFile = ts.createSourceFile('test.ts', sourceCode, ts.ScriptTarget.Latest, true); + const sourceFile = await createSourceFile(sourceCode); const variableStatement = sourceFile.statements[0]; if (!ts.isVariableStatement(variableStatement)) { throw new Error('Expected variable statement'); @@ -970,9 +989,42 @@ describe('TSCompilerUtils', () => { }); }); + it('transforms children before replacing their parent and skips removed subtrees', async () => { + // Given nested properties, a parent replacement must retain translated children and omit removed branches. + const source = await createSourceFile("const strings = {keep: {value: 'Hello'}, remove: {value: 'Skip'}};"); + const visitedPaths: string[] = []; + const transform = TSCompilerUtils.createPathAwareTransformer((node, translationPath) => { + visitedPaths.push(translationPath); + if (translationPath === 'remove') { + return {action: TransformerAction.Remove}; + } + if (ts.isStringLiteral(node)) { + return {action: TransformerAction.Replace, newNode: () => factory.createStringLiteral('Translated', ts.TokenFlags.None)}; + } + if (ts.isPropertyAssignment(node) && translationPath === 'keep') { + return { + action: TransformerAction.Replace, + newNode: (transformed) => { + const property = expectNode(transformed, ts.isPropertyAssignment, 'transformed parent'); + return TSCompilerUtils.createPropertyAssignment('renamed', property.initializer); + }, + }; + } + return {action: TransformerAction.Continue}; + }); + + // When the native visitor rebuilds the source file, replacement callbacks receive the transformed children. + const output = await printSourceFile(transform(source)); + + // Then parent replacement keeps the child change, and removal prevents visiting any descendants. + expect(output).toContain('renamed: { value: "Translated"'); + expect(output).not.toContain('remove'); + expect(visitedPaths).not.toContain('remove.value'); + }); + describe('objectHas', () => { - it('returns true for top-level property that exists', () => { - const source = createSourceFile( + it('returns true for top-level property that exists', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { greeting: 'Hello', @@ -987,8 +1039,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'farewell')).toBe(true); }); - it('returns false for top-level property that does not exist', () => { - const source = createSourceFile( + it('returns false for top-level property that does not exist', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { greeting: 'Hello', @@ -1001,8 +1053,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'nonexistent')).toBe(false); }); - it('returns true for nested property that exists', () => { - const source = createSourceFile( + it('returns true for nested property that exists', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { common: { @@ -1022,8 +1074,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'errors.generic')).toBe(true); }); - it('returns false for nested property that does not exist', () => { - const source = createSourceFile( + it('returns false for nested property that does not exist', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { common: { @@ -1043,8 +1095,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'errors.nonexistent')).toBe(false); }); - it('returns false when trying to traverse into a non-object property', () => { - const source = createSourceFile( + it('returns false when trying to traverse into a non-object property', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { greeting: 'Hello', @@ -1060,8 +1112,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'greeting.something')).toBe(false); }); - it('returns true for deeply nested properties', () => { - const source = createSourceFile( + it('returns true for deeply nested properties', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { level1: { @@ -1079,8 +1131,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'level1.level2.level3.deepProperty')).toBe(true); }); - it('returns false for partially correct deeply nested paths', () => { - const source = createSourceFile( + it('returns false for partially correct deeply nested paths', async () => { + const source = await createSourceFile( Str.dedent(` const obj = { level1: { @@ -1099,8 +1151,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.objectHas(objectLiteral, 'level1.level2.wrongLevel.deepProperty')).toBe(false); }); - it('handles empty object', () => { - const source = createSourceFile( + it('handles empty object', async () => { + const source = await createSourceFile( Str.dedent(` const obj = {}; `), @@ -1119,7 +1171,7 @@ describe('TSCompilerUtils', () => { objectLiteral = node; return; } - ts.forEachChild(node, visit); + node.forEachChild(visit); } visit(sourceFile); @@ -1133,10 +1185,10 @@ describe('TSCompilerUtils', () => { describe('injectDeepObjectValue', () => { it('should inject value into basic nested structure', () => { // Start with empty object - const objectLiteral = ts.factory.createObjectLiteralExpression([]); + const objectLiteral = factory.createObjectLiteralExpression([]); // Create a final value - const finalValue = ts.factory.createStringLiteral('test value'); + const finalValue = factory.createStringLiteral('test value', ts.TokenFlags.None); // Add nested path const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'test.nested', finalValue); @@ -1156,10 +1208,10 @@ describe('TSCompilerUtils', () => { it('should throw error when trying to inject into non-object property', () => { // Start with existing property that's not an object - const existingProperty = ts.factory.createPropertyAssignment('existing', ts.factory.createStringLiteral('value')); - const objectLiteral = ts.factory.createObjectLiteralExpression([existingProperty]); + const existingProperty = TSCompilerUtils.createPropertyAssignment('existing', factory.createStringLiteral('value', ts.TokenFlags.None)); + const objectLiteral = factory.createObjectLiteralExpression([existingProperty]); - const finalValue = ts.factory.createStringLiteral('new value'); + const finalValue = factory.createStringLiteral('new value', ts.TokenFlags.None); // Try to add nested path under a non-object property expect(() => { @@ -1169,11 +1221,13 @@ describe('TSCompilerUtils', () => { it('should inject into existing nested object structure', () => { // Start with existing nested structure - const nestedObj = ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment('existingNested', ts.factory.createStringLiteral('existing value'))]); - const existingProperty = ts.factory.createPropertyAssignment('existing', nestedObj); - const objectLiteral = ts.factory.createObjectLiteralExpression([existingProperty]); + const nestedObj = factory.createObjectLiteralExpression([ + TSCompilerUtils.createPropertyAssignment('existingNested', factory.createStringLiteral('existing value', ts.TokenFlags.None)), + ]); + const existingProperty = TSCompilerUtils.createPropertyAssignment('existing', nestedObj); + const objectLiteral = factory.createObjectLiteralExpression([existingProperty]); - const finalValue = ts.factory.createStringLiteral('new value'); + const finalValue = factory.createStringLiteral('new value', ts.TokenFlags.None); // Add new path under existing structure const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'existing.newNested', finalValue); @@ -1197,10 +1251,10 @@ describe('TSCompilerUtils', () => { it('should replace existing value when path points to existing property', () => { // Start with existing nested structure - const existingProperty = ts.factory.createPropertyAssignment('existing', ts.factory.createStringLiteral('old value')); - const objectLiteral = ts.factory.createObjectLiteralExpression([existingProperty]); + const existingProperty = TSCompilerUtils.createPropertyAssignment('existing', factory.createStringLiteral('old value', ts.TokenFlags.None)); + const objectLiteral = factory.createObjectLiteralExpression([existingProperty]); - const newValue = ts.factory.createStringLiteral('new value'); + const newValue = factory.createStringLiteral('new value', ts.TokenFlags.None); // Replace existing value const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'existing', newValue); @@ -1213,8 +1267,8 @@ describe('TSCompilerUtils', () => { }); it('should handle single-level paths', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); - const finalValue = ts.factory.createStringLiteral('single value'); + const objectLiteral = factory.createObjectLiteralExpression([]); + const finalValue = factory.createStringLiteral('single value', ts.TokenFlags.None); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'single', finalValue); @@ -1225,8 +1279,8 @@ describe('TSCompilerUtils', () => { }); it('should handle deep nested paths', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); - const finalValue = ts.factory.createStringLiteral('deep value'); + const objectLiteral = factory.createObjectLiteralExpression([]); + const finalValue = factory.createStringLiteral('deep value', ts.TokenFlags.None); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'a.b.c.d', finalValue); @@ -1254,12 +1308,12 @@ describe('TSCompilerUtils', () => { it('should preserve existing properties when adding new ones', () => { // Start with multiple existing properties const existingProps = [ - ts.factory.createPropertyAssignment('first', ts.factory.createStringLiteral('first value')), - ts.factory.createPropertyAssignment('second', ts.factory.createStringLiteral('second value')), + TSCompilerUtils.createPropertyAssignment('first', factory.createStringLiteral('first value', ts.TokenFlags.None)), + TSCompilerUtils.createPropertyAssignment('second', factory.createStringLiteral('second value', ts.TokenFlags.None)), ]; - const objectLiteral = ts.factory.createObjectLiteralExpression(existingProps); + const objectLiteral = factory.createObjectLiteralExpression(existingProps); - const finalValue = ts.factory.createStringLiteral('new nested value'); + const finalValue = factory.createStringLiteral('new nested value', ts.TokenFlags.None); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'newSection.nested', finalValue); @@ -1281,11 +1335,11 @@ describe('TSCompilerUtils', () => { }); it('should handle complex expressions as final values', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); + const objectLiteral = factory.createObjectLiteralExpression([]); // Create a template literal expression - const finalValue = ts.factory.createTemplateExpression(ts.factory.createTemplateHead('Hello '), [ - ts.factory.createTemplateSpan(ts.factory.createIdentifier('name'), ts.factory.createTemplateTail('!')), + const finalValue = factory.createTemplateExpression(factory.createTemplateHead('Hello ', '', ts.TokenFlags.None), [ + factory.createTemplateSpan(factory.createIdentifier('name'), factory.createTemplateTail('!', '', ts.TokenFlags.None)), ]); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'test.greeting', finalValue); @@ -1299,16 +1353,16 @@ describe('TSCompilerUtils', () => { }); it('should handle arrow function expressions as final values', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); + const objectLiteral = factory.createObjectLiteralExpression([]); // Create an arrow function expression - const finalValue = ts.factory.createArrowFunction( + const finalValue = factory.createArrowFunction( undefined, undefined, - [ts.factory.createParameterDeclaration(undefined, undefined, 'name')], + [factory.createParameterDeclaration(undefined, undefined, factory.createIdentifier('name'))], undefined, - ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), - ts.factory.createStringLiteral('Hello'), + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + factory.createStringLiteral('Hello', ts.TokenFlags.None), ); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'test.func', finalValue); @@ -1322,9 +1376,9 @@ describe('TSCompilerUtils', () => { }); it('should throw error for empty path', () => { - const existingProperty = ts.factory.createPropertyAssignment('existing', ts.factory.createStringLiteral('value')); - const objectLiteral = ts.factory.createObjectLiteralExpression([existingProperty]); - const finalValue = ts.factory.createStringLiteral('ignored'); + const existingProperty = TSCompilerUtils.createPropertyAssignment('existing', factory.createStringLiteral('value', ts.TokenFlags.None)); + const objectLiteral = factory.createObjectLiteralExpression([existingProperty]); + const finalValue = factory.createStringLiteral('ignored', ts.TokenFlags.None); expect(() => { TSCompilerUtils.injectDeepObjectValue(objectLiteral, '', finalValue); @@ -1332,8 +1386,8 @@ describe('TSCompilerUtils', () => { }); it('should throw error for paths with empty parts', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); - const finalValue = ts.factory.createStringLiteral('test value'); + const objectLiteral = factory.createObjectLiteralExpression([]); + const finalValue = factory.createStringLiteral('test value', ts.TokenFlags.None); // Path with empty part (double dot) should throw error expect(() => { @@ -1342,8 +1396,8 @@ describe('TSCompilerUtils', () => { }); it('should throw error for paths starting with dot', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); - const finalValue = ts.factory.createStringLiteral('test value'); + const objectLiteral = factory.createObjectLiteralExpression([]); + const finalValue = factory.createStringLiteral('test value', ts.TokenFlags.None); // Path starting with dot should throw error expect(() => { @@ -1352,8 +1406,8 @@ describe('TSCompilerUtils', () => { }); it('should handle numeric-like property names', () => { - const objectLiteral = ts.factory.createObjectLiteralExpression([]); - const finalValue = ts.factory.createStringLiteral('numeric value'); + const objectLiteral = factory.createObjectLiteralExpression([]); + const finalValue = factory.createStringLiteral('numeric value', ts.TokenFlags.None); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, '123.456', finalValue); @@ -1369,17 +1423,19 @@ describe('TSCompilerUtils', () => { it('should handle satisfies expressions in nested objects', () => { // Create object: { tasks: { existing: 'value' } satisfies Record } - const satisfiesExpr = ts.factory.createSatisfiesExpression( - ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(ts.factory.createIdentifier('existing'), ts.factory.createStringLiteral('value'))]), - ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [ - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + const satisfiesExpr = factory.createSatisfiesExpression( + factory.createObjectLiteralExpression([ + TSCompilerUtils.createPropertyAssignment(factory.createIdentifier('existing'), factory.createStringLiteral('value', ts.TokenFlags.None)), + ]), + factory.createTypeReferenceNode(factory.createIdentifier('Record'), [ + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), ]), ); - const objectLiteral = ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(ts.factory.createIdentifier('tasks'), satisfiesExpr)]); + const objectLiteral = factory.createObjectLiteralExpression([TSCompilerUtils.createPropertyAssignment(factory.createIdentifier('tasks'), satisfiesExpr)]); - const newValue = ts.factory.createStringLiteral('new value'); + const newValue = factory.createStringLiteral('new value', ts.TokenFlags.None); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'tasks.newKey', newValue); // Verify satisfies expression is preserved @@ -1393,17 +1449,19 @@ describe('TSCompilerUtils', () => { it('should replace existing values in satisfies expressions', () => { // Create object: { nested: { existingKey: 'old value' } satisfies Record } - const satisfiesExpr = ts.factory.createSatisfiesExpression( - ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(ts.factory.createIdentifier('existingKey'), ts.factory.createStringLiteral('old value'))]), - ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Record'), [ - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + const satisfiesExpr = factory.createSatisfiesExpression( + factory.createObjectLiteralExpression([ + TSCompilerUtils.createPropertyAssignment(factory.createIdentifier('existingKey'), factory.createStringLiteral('old value', ts.TokenFlags.None)), + ]), + factory.createTypeReferenceNode(factory.createIdentifier('Record'), [ + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), ]), ); - const objectLiteral = ts.factory.createObjectLiteralExpression([ts.factory.createPropertyAssignment(ts.factory.createIdentifier('nested'), satisfiesExpr)]); + const objectLiteral = factory.createObjectLiteralExpression([TSCompilerUtils.createPropertyAssignment(factory.createIdentifier('nested'), satisfiesExpr)]); - const newValue = ts.factory.createStringLiteral('new value'); + const newValue = factory.createStringLiteral('new value', ts.TokenFlags.None); const updatedObject = TSCompilerUtils.injectDeepObjectValue(objectLiteral, 'nested.existingKey', newValue); // Verify satisfies expression is preserved and value is replaced @@ -1419,100 +1477,190 @@ describe('TSCompilerUtils', () => { describe('isStringConcatenationChain', () => { it('should return true for simple string concatenation', () => { // 'hello' + 'world' - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('hello'), ts.SyntaxKind.PlusToken, ts.factory.createStringLiteral('world')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('hello', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral('world', ts.TokenFlags.None), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(true); }); it('should return true for string + template literal', () => { // 'hello' + `world` - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('hello'), ts.SyntaxKind.PlusToken, ts.factory.createNoSubstitutionTemplateLiteral('world')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('hello', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createNoSubstitutionTemplateLiteral('world', ts.TokenFlags.None), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(true); }); it('should return true for template literal + string', () => { // `hello` + 'world' - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createNoSubstitutionTemplateLiteral('hello'), ts.SyntaxKind.PlusToken, ts.factory.createStringLiteral('world')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createNoSubstitutionTemplateLiteral('hello', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral('world', ts.TokenFlags.None), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(true); }); it('should return true for complex string concatenation chain', () => { // 'a' + 'b' + 'c' - const innerBinary = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('a'), ts.SyntaxKind.PlusToken, ts.factory.createStringLiteral('b')); - const outerBinary = ts.factory.createBinaryExpression(innerBinary, ts.SyntaxKind.PlusToken, ts.factory.createStringLiteral('c')); + const innerBinary = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('a', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral('b', ts.TokenFlags.None), + ); + const outerBinary = factory.createBinaryExpression( + undefined, + innerBinary, + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral('c', ts.TokenFlags.None), + ); expect(TSCompilerUtils.isStringConcatenationChain(outerBinary)).toBe(true); }); it('should return true for left-nested string concatenation', () => { // ('a' + 'b') + variable - const leftBinary = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('a'), ts.SyntaxKind.PlusToken, ts.factory.createStringLiteral('b')); - const outerBinary = ts.factory.createBinaryExpression(leftBinary, ts.SyntaxKind.PlusToken, ts.factory.createIdentifier('variable')); + const leftBinary = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('a', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral('b', ts.TokenFlags.None), + ); + const outerBinary = factory.createBinaryExpression(undefined, leftBinary, undefined, factory.createToken(ts.SyntaxKind.PlusToken), factory.createIdentifier('variable')); expect(TSCompilerUtils.isStringConcatenationChain(outerBinary)).toBe(true); }); it('should return false for variable + string concatenation (ambiguous)', () => { // variable + ('a' + 'b') - could be numeric or string, so should return false - const rightBinary = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('a'), ts.SyntaxKind.PlusToken, ts.factory.createStringLiteral('b')); - const outerBinary = ts.factory.createBinaryExpression(ts.factory.createIdentifier('variable'), ts.SyntaxKind.PlusToken, rightBinary); + const rightBinary = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('a', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral('b', ts.TokenFlags.None), + ); + const outerBinary = factory.createBinaryExpression( + undefined, + factory.createIdentifier('variable'), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createParenthesizedExpression(rightBinary), + ); expect(TSCompilerUtils.isStringConcatenationChain(outerBinary)).toBe(false); }); it('should return true for string + variable concatenation', () => { // 'hello' + variable - definitely string concatenation - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('hello'), ts.SyntaxKind.PlusToken, ts.factory.createIdentifier('variable')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('hello', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createIdentifier('variable'), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(true); }); it('should return false for numeric addition', () => { // 1 + 2 - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createNumericLiteral('1'), ts.SyntaxKind.PlusToken, ts.factory.createNumericLiteral('2')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createNumericLiteral('1', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createNumericLiteral('2', ts.TokenFlags.None), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(false); }); it('should return false for variable addition', () => { // a + b - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createIdentifier('a'), ts.SyntaxKind.PlusToken, ts.factory.createIdentifier('b')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createIdentifier('a'), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createIdentifier('b'), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(false); }); it('should return false for non-plus operators', () => { // 'hello' - 'world' - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('hello'), ts.SyntaxKind.MinusToken, ts.factory.createStringLiteral('world')); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('hello', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.MinusToken), + factory.createStringLiteral('world', ts.TokenFlags.None), + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(false); }); it('should return true for template expressions with substitutions', () => { // 'hello' + `world ${variable}` - const templateExpr = ts.factory.createTemplateExpression(ts.factory.createTemplateHead('world '), [ - ts.factory.createTemplateSpan(ts.factory.createIdentifier('variable'), ts.factory.createTemplateTail('')), + const templateExpr = factory.createTemplateExpression(factory.createTemplateHead('world ', '', ts.TokenFlags.None), [ + factory.createTemplateSpan(factory.createIdentifier('variable'), factory.createTemplateTail('', '', ts.TokenFlags.None)), ]); - const binaryExpr = ts.factory.createBinaryExpression(ts.factory.createStringLiteral('hello'), ts.SyntaxKind.PlusToken, templateExpr); + const binaryExpr = factory.createBinaryExpression( + undefined, + factory.createStringLiteral('hello', ts.TokenFlags.None), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + templateExpr, + ); expect(TSCompilerUtils.isStringConcatenationChain(binaryExpr)).toBe(true); }); it('should return false for complex nested non-string expressions', () => { // (a + b) + (c + d) where all are variables - const leftBinary = ts.factory.createBinaryExpression(ts.factory.createIdentifier('a'), ts.SyntaxKind.PlusToken, ts.factory.createIdentifier('b')); - const rightBinary = ts.factory.createBinaryExpression(ts.factory.createIdentifier('c'), ts.SyntaxKind.PlusToken, ts.factory.createIdentifier('d')); - const outerBinary = ts.factory.createBinaryExpression(leftBinary, ts.SyntaxKind.PlusToken, rightBinary); + const leftBinary = factory.createBinaryExpression( + undefined, + factory.createIdentifier('a'), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createIdentifier('b'), + ); + const rightBinary = factory.createBinaryExpression( + undefined, + factory.createIdentifier('c'), + undefined, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createIdentifier('d'), + ); + const outerBinary = factory.createBinaryExpression(undefined, leftBinary, undefined, factory.createToken(ts.SyntaxKind.PlusToken), rightBinary); expect(TSCompilerUtils.isStringConcatenationChain(outerBinary)).toBe(false); }); }); describe('getIndentationOfNode', () => { - it('should return 0 for a node at column 0', () => { - const source = createSourceFile( + it('should return 0 for a node at column 0', async () => { + const source = await createSourceFile( Str.dedent(` const x = 1; `), @@ -1522,8 +1670,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.getIndentationOfNode(statement, source)).toBe(0); }); - it('should return correct indentation for a node with leading spaces', () => { - const source = createSourceFile( + it('should return correct indentation for a node with leading spaces', async () => { + const source = await createSourceFile( Str.dedent(` function example() { const x = 1; @@ -1538,8 +1686,8 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.getIndentationOfNode(constStatement, source)).toBe(4); }); - it('should handle nested indentation', () => { - const source = createSourceFile( + it('should handle nested indentation', async () => { + const source = await createSourceFile( Str.dedent(` class MyClass { method() { @@ -1561,15 +1709,15 @@ describe('TSCompilerUtils', () => { expect(TSCompilerUtils.getIndentationOfNode(constStatement, source)).toBe(12); }); - it('should handle tabs as single characters', () => { - const source = createSourceFile('\t\tconst x = 1;'); + it('should handle tabs as single characters', async () => { + const source = await createSourceFile('\t\tconst x = 1;'); const statement = source.statements[0]; expect(TSCompilerUtils.getIndentationOfNode(statement, source)).toBe(2); }); - it('should handle mixed tabs and spaces', () => { - const source = createSourceFile('\t const x = 1;'); + it('should handle mixed tabs and spaces', async () => { + const source = await createSourceFile('\t const x = 1;'); const statement = source.statements[0]; expect(TSCompilerUtils.getIndentationOfNode(statement, source)).toBe(5); diff --git a/tests/tooling/generateTranslations.test.ts b/tests/tooling/generateTranslations.test.ts index 83e8041c305c..a781629ffab6 100644 --- a/tests/tooling/generateTranslations.test.ts +++ b/tests/tooling/generateTranslations.test.ts @@ -410,6 +410,7 @@ describe('generateTranslations', () => { }); it('does not include any comments in generated translation files', async () => { + // Given context comments and literal comment markers, only actual comments should be removed. fs.writeFileSync( EN_PATH, Str.dedent(` @@ -424,19 +425,23 @@ describe('generateTranslations', () => { /* @context inline multi-line */ inline: 'Inline', // eslint-disable-next-line max-len longString: 'This is a very long string', + literalMarkers: 'https://example.com/*literal*/', + templateMarkers: (name: string) => \`Hello \${name} // literal /* text */\`, }; export default strings; `), 'utf8', ); + // When the native printer emits translations, parsed trivia distinguishes comments from string content. await generateTranslations(); const itContent = fs.readFileSync(IT_PATH, 'utf8'); - // Verify no comments of any kind appear in the output (except the generated file prefix) + // Then comments disappear without removing literal markers from translated strings or templates. const contentAfterPrefix = itContent.substring(GENERATED_FILE_PREFIX.length); - expect(contentAfterPrefix).not.toContain('//'); - expect(contentAfterPrefix).not.toContain('/*'); - expect(contentAfterPrefix).not.toContain('*/'); + const contentWithoutMarkers = contentAfterPrefix.replaceAll('https://example.com/*literal*/', '').replaceAll('// literal /* text */', ''); + expect(contentWithoutMarkers).not.toContain('//'); + expect(contentWithoutMarkers).not.toContain('/*'); + expect(contentWithoutMarkers).not.toContain('*/'); expect(contentAfterPrefix).not.toContain('@context'); expect(contentAfterPrefix).not.toContain('eslint-disable'); @@ -444,6 +449,8 @@ describe('generateTranslations', () => { expect(itContent).toContain('[it][ctx: As in a financial institution] Bank'); expect(itContent).toContain('[it] Hello'); expect(itContent).toContain('[it] Goodbye'); + expect(itContent).toContain('[it] https://example.com/*literal*/'); + expect(itContent).toContain(`[it] Hello \${name} // literal /* text */`); }); it("doesn't request duplicate translations", async () => { From 426ddc702549700127abe0e8aa54062c3a163fc2 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 13:49:00 -0800 Subject: [PATCH 06/10] fix: preserve tagged template raw spelling --- scripts/utils/TSCompilerUtils.ts | 45 ++++++++++++++++++++++++--- tests/tooling/TSCompilerUtils.test.ts | 28 +++++++++++++++++ 2 files changed, 69 insertions(+), 4 deletions(-) diff --git a/scripts/utils/TSCompilerUtils.ts b/scripts/utils/TSCompilerUtils.ts index e74f1d9c5ecf..b89e9344c731 100644 --- a/scripts/utils/TSCompilerUtils.ts +++ b/scripts/utils/TSCompilerUtils.ts @@ -3,6 +3,21 @@ import type {API} from 'typescript/async'; import * as ts from 'typescript/ast'; import * as factory from 'typescript/ast/factory'; import * as is from 'typescript/ast/is'; +import {isRemoteNode} from 'typescript/async'; + +const taggedTemplateRawText = new WeakMap(); + +function getTaggedTemplatesWithoutSubstitutions(node: ts.Node): ts.NoSubstitutionTemplateLiteral[] { + const templates: ts.NoSubstitutionTemplateLiteral[] = []; + const visit = (child: ts.Node): void => { + if (ts.isTaggedTemplateExpression(child) && ts.isNoSubstitutionTemplateLiteral(child.template)) { + templates.push(child.template); + } + child.forEachChild(visit); + }; + visit(node); + return templates; +} /** * Actions that can be taken when visiting a node in a transformer. @@ -290,8 +305,17 @@ async function parseCodeStringToAST(codeString: string, api: API): Promise { + // The beta printer cooks tagged templates without substitutions, changing the raw strings passed to their tags. + const rawTemplates = getTaggedTemplatesWithoutSubstitutions(node).map((template) => (isRemoteNode(template) ? template.getText() : taggedTemplateRawText.get(template))); const code = await api.printer.printNode(node, {preserveSourceNewlines: false}); const lease = await api.createSourceFile('printed.ts', code); try { @@ -532,9 +558,20 @@ async function printWithoutComments(api: API, node: ts.Node): Promise { } }; collectComments(lease.sourceFile); + const replacements = [...ranges].map(([start, end]) => ({start, end, text: code.slice(start, end).replaceAll(/[^\r\n]/g, ' ')})); + const printedTemplates = getTaggedTemplatesWithoutSubstitutions(lease.sourceFile); + if (printedTemplates.length !== rawTemplates.length) { + throw new Error('Could not preserve tagged template raw text after printing'); + } + for (const [index, template] of printedTemplates.entries()) { + const rawText = rawTemplates.at(index); + if (rawText !== undefined) { + replacements.push({start: template.getStart(), end: template.end, text: rawText}); + } + } let result = code; - for (const [start, end] of [...ranges].sort(([left], [right]) => right - left)) { - result = result.slice(0, start) + result.slice(start, end).replaceAll(/[^\r\n]/g, ' ') + result.slice(end); + for (const {start, end, text} of replacements.sort((left, right) => right.start - left.start)) { + result = result.slice(0, start) + text + result.slice(end); } return result; } finally { diff --git a/tests/tooling/TSCompilerUtils.test.ts b/tests/tooling/TSCompilerUtils.test.ts index 87ca5ae3f85c..1038d7852872 100644 --- a/tests/tooling/TSCompilerUtils.test.ts +++ b/tests/tooling/TSCompilerUtils.test.ts @@ -3,6 +3,7 @@ import {afterEach, beforeEach, describe, expect, it} from 'bun:test'; import type {RetainedSourceFile} from 'typescript/async'; import {Str} from 'expensify-common'; +import {runInNewContext} from 'node:vm'; import * as ts from 'typescript/ast'; import * as factory from 'typescript/ast/factory'; import {API} from 'typescript/async'; @@ -1022,6 +1023,33 @@ describe('TSCompilerUtils', () => { expect(visitedPaths).not.toContain('remove.value'); }); + it('preserves evaluated raw and cooked values for arbitrary template tags after round trips', async () => { + // Given escaped tagged templates, tags can observe spelling that differs from the cooked string value. + const code = Str.dedent(` + (() => { + const capture = (strings, ...values) => ({raw: [...strings.raw], cooked: [...strings], values}); + return [ + String.raw\`a\\nb\`, + capture\`a\\tb /* literal */\`, + capture\`before\\n\${'value'}after\\t\`, + capture\`outer\${capture\`nested\\n\`}end\\t\`, + ]; + })() + `); + const expected: unknown = runInNewContext(code); + const sourceFile = await createSourceFile(code); + + // When an incremental patch is parsed, its detached clone must keep raw spelling after the lease is disposed. + const clone = await TSCompilerUtils.parseCodeStringToAST(code, api); + for (const node of [sourceFile, clone]) { + const printed = await TSCompilerUtils.printWithoutComments(api, node); + const actual: unknown = runInNewContext(printed); + + // Then both untouched source and detached patches pass exactly the same raw, cooked and substitution values to tags. + expect(actual).toEqual(expected); + } + }); + describe('objectHas', () => { it('returns true for top-level property that exists', async () => { const source = await createSourceFile( From 5e51b8af18b33361a58534a9dc48f6c6ed3324e5 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 13:52:47 -0800 Subject: [PATCH 07/10] build(lint): remove legacy compiler consumers Keep hosted rule implementations and fixture scope analysis without a\nJavaScript TypeScript compiler. Preserve existing lint diagnostics and\nremove the seven compiler-routing patches. --- config/oxlint/plugins/expensify-rules.mjs | 7 +- config/oxlint/plugins/hosted-rules.mjs | 57 +- .../oxlint/preferLocaleCompareFromContext.mjs | 22 +- .../@typescript-eslint-eslint-plugin-LICENSE | 21 + .../vendor/@typescript-eslint-utils-LICENSE | 21 + config/oxlint/vendor/README.md | 51 + config/oxlint/vendor/build.mjs | 140 + .../vendor/eslint-plugin-storybook-LICENSE | 21 + .../eslint-plugin-testing-library-LICENSE | 21 + config/oxlint/vendor/expensify-rules.mjs | 2464 ++++++++++++++++ config/oxlint/vendor/naming-convention.mjs | 1435 ++++++++++ config/oxlint/vendor/storybook.mjs | 1502 ++++++++++ .../oxlint/vendor/syntax-rule-utilities.mjs | 639 +++++ config/oxlint/vendor/testing-library.mjs | 2466 +++++++++++++++++ contributingGuides/LINTING.md | 2 +- cspell.json | 2 + oxlint.config.mts | 8 +- ...lugin+8.58.1+001+require-typescript6.patch | 715 ----- .../eslint-plugin/details.md | 15 - ...arser+8.58.1+001+require-typescript6.patch | 13 - patches/@typescript-eslint/parser/details.md | 15 - ...rvice+8.61.0+001+require-typescript6.patch | 13 - .../project-service/details.md | 15 - ...utils+8.58.1+001+require-typescript6.patch | 143 - .../@typescript-eslint/type-utils/details.md | 15 - ...stree+8.61.0+001+require-typescript6.patch | 234 -- .../typescript-estree/details.md | 15 - patches/eslint-config-expensify/details.md | 15 - ...pensify+4.0.8+001+import-typescript6.patch | 12 - patches/ts-api-utils/details.md | 15 - ...-utils+2.5.0+001+require-typescript6.patch | 22 - .../NoDirectPersonalDetailsListRuleTest.ts | 3 +- .../NoLayoutSpacingConditionalRuleTest.ts | 3 +- tests/unit/NoOnyxGetSnapshotKeyRuleTest.ts | 3 +- tests/unit/NoOnyxLoadingStatusRuleTest.ts | 3 +- tests/unit/NoRawTypographyRuleTest.ts | 3 +- tests/unit/NoUnsafeOnyxReadRuleTest.ts | 3 +- ...ireLocaleForLocalizedDateFormatRuleTest.ts | 3 +- tests/utils/typeScriptRuleTestParser.d.ts | 5 + tests/utils/typeScriptRuleTestParser.js | 86 + 40 files changed, 8904 insertions(+), 1344 deletions(-) create mode 100644 config/oxlint/vendor/@typescript-eslint-eslint-plugin-LICENSE create mode 100644 config/oxlint/vendor/@typescript-eslint-utils-LICENSE create mode 100644 config/oxlint/vendor/README.md create mode 100644 config/oxlint/vendor/build.mjs create mode 100644 config/oxlint/vendor/eslint-plugin-storybook-LICENSE create mode 100644 config/oxlint/vendor/eslint-plugin-testing-library-LICENSE create mode 100644 config/oxlint/vendor/expensify-rules.mjs create mode 100644 config/oxlint/vendor/naming-convention.mjs create mode 100644 config/oxlint/vendor/storybook.mjs create mode 100644 config/oxlint/vendor/syntax-rule-utilities.mjs create mode 100644 config/oxlint/vendor/testing-library.mjs delete mode 100644 patches/@typescript-eslint/eslint-plugin/@typescript-eslint+eslint-plugin+8.58.1+001+require-typescript6.patch delete mode 100644 patches/@typescript-eslint/eslint-plugin/details.md delete mode 100644 patches/@typescript-eslint/parser/@typescript-eslint+parser+8.58.1+001+require-typescript6.patch delete mode 100644 patches/@typescript-eslint/parser/details.md delete mode 100644 patches/@typescript-eslint/project-service/@typescript-eslint+project-service+8.61.0+001+require-typescript6.patch delete mode 100644 patches/@typescript-eslint/project-service/details.md delete mode 100644 patches/@typescript-eslint/type-utils/@typescript-eslint+type-utils+8.58.1+001+require-typescript6.patch delete mode 100644 patches/@typescript-eslint/type-utils/details.md delete mode 100644 patches/@typescript-eslint/typescript-estree/@typescript-eslint+typescript-estree+8.61.0+001+require-typescript6.patch delete mode 100644 patches/@typescript-eslint/typescript-estree/details.md delete mode 100644 patches/eslint-config-expensify/details.md delete mode 100644 patches/eslint-config-expensify/eslint-config-expensify+4.0.8+001+import-typescript6.patch delete mode 100644 patches/ts-api-utils/details.md delete mode 100644 patches/ts-api-utils/ts-api-utils+2.5.0+001+require-typescript6.patch create mode 100644 tests/utils/typeScriptRuleTestParser.d.ts create mode 100644 tests/utils/typeScriptRuleTestParser.js diff --git a/config/oxlint/plugins/expensify-rules.mjs b/config/oxlint/plugins/expensify-rules.mjs index 2611ab96d110..1d0489d8a783 100644 --- a/config/oxlint/plugins/expensify-rules.mjs +++ b/config/oxlint/plugins/expensify-rules.mjs @@ -1,5 +1,5 @@ /** - * Loads every rule from eslint-plugin-expensify and eslint-plugin-local-rules into oxlint, under the + * Loads the enabled Expensify rules and eslint-plugin-local-rules into oxlint, under the * `rulesdir` name ESLint uses. Two rules change: no-inline-useOnyx-selector stays quiet in files React * Compiler memoizes, and prefer-locale-compare-from-context is swapped for a copy that needs no type info. */ @@ -10,15 +10,16 @@ import {fileURLToPath} from 'node:url'; import {create as preferLocaleCompareCreate, meta as preferLocaleCompareMeta} from '../preferLocaleCompareFromContext.mjs'; import {withFullGating} from '../reactCompilerGate.mjs'; +import {rules as expensifyRules} from '../vendor/expensify-rules.mjs'; const require = createRequire(import.meta.url); const pluginDir = path.dirname(fileURLToPath(import.meta.url)); -const RULE_DIRS = [path.resolve(pluginDir, '../../../node_modules/eslint-config-expensify/eslint-plugin-expensify'), path.resolve(pluginDir, '../../../eslint-plugin-local-rules')]; +const RULE_DIRS = [path.resolve(pluginDir, '../../../eslint-plugin-local-rules')]; const HELPER_FILES = new Set(['CONST.js']); -const rules = {}; +const rules = {...expensifyRules}; for (const dir of RULE_DIRS) { for (const file of fs.readdirSync(dir)) { if (!file.endsWith('.js') || HELPER_FILES.has(file)) { diff --git a/config/oxlint/plugins/hosted-rules.mjs b/config/oxlint/plugins/hosted-rules.mjs index f62e75940ebd..4f4540cc0ebc 100644 --- a/config/oxlint/plugins/hosted-rules.mjs +++ b/config/oxlint/plugins/hosted-rules.mjs @@ -1,5 +1,5 @@ /** - * Runs rules from ESLint plugins (react, import, jsdoc, storybook, react-hooks, typescript-eslint) + * Runs rules from ESLint plugins (react, import, jsdoc, react-hooks) and syntax-only hosted rules * inside oxlint, using the plugins' own code. Each one is here because oxlint has no native version or * its native one reports differently. oxlint reserves most of these plugin names for its own ports, * so all of them load under `hosted`. @@ -13,6 +13,8 @@ import {withEslintDirectiveIDs, withEslintDirectiveIDsFor} from '../eslintDirect import {create as noDeprecatedSelfClosingJsxCreate, meta as noDeprecatedSelfClosingJsxMeta} from '../noDeprecatedSelfClosingJsx.mjs'; import {withFullGating, withMessageGating} from '../reactCompilerGate.mjs'; import {hostedRuleNames} from '../ruleNames.mjs'; +import namingConvention from '../vendor/naming-convention.mjs'; +import storybookPlugin from '../vendor/storybook.mjs'; const require = createRequire(import.meta.url); const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../..'); @@ -27,8 +29,7 @@ function rulesOf(packageName) { try { mod = require(path.resolve(repoRoot, 'node_modules', packageName)); } catch { - // A directory require ignores `exports`, so a package with no `main` (typescript-eslint) needs - // the bare specifier. + // A directory require ignores `exports`, so packages with no `main` need their bare specifier. mod = require(packageName); } return mod?.rules ?? mod?.default?.rules ?? {}; @@ -37,52 +38,8 @@ function rulesOf(packageName) { const react = rulesOf('eslint-plugin-react'); const importPlugin = rulesOf('eslint-plugin-import'); const jsdoc = rulesOf('eslint-plugin-jsdoc'); -const storybook = rulesOf('eslint-plugin-storybook'); -const typescriptEslint = rulesOf('@typescript-eslint/eslint-plugin'); - -// eslint-plugin-react-hooks is not a root dependency; ESLint loads the copy nested under -// eslint-config-expensify. -const reactHooks = (() => { - const configEntry = require.resolve('eslint-config-expensify', {paths: [repoRoot]}); - return rulesOf(require.resolve('eslint-plugin-react-hooks', {paths: [path.dirname(configEntry)]})); -})(); - -// naming-convention requires parser services, which oxlint's JS plugins lack. -// getParserServices(context, true) tolerates a missing `program` but fails on missing node maps and -// reads context.languageOptions first, so both are stubbed. This repo's selectors never reach the -// type checker (no `types` selector). -const STUB_PARSER_SERVICES = { - esTreeNodeToTSNodeMap: new Map(), - tsNodeToESTreeNodeMap: new Map(), -}; - -/** - * Lets typescript-eslint's naming-convention run in oxlint. The rule fails without TypeScript parser - * services, which oxlint's JS plugins do not have, so it gets STUB_PARSER_SERVICES instead. - * @param {import('eslint').Rule.RuleModule} rule - * @returns {import('eslint').Rule.RuleModule} - */ -function withStubbedParserServices(rule) { - return { - ...rule, - create(context) { - const sourceCode = context.sourceCode ?? context.getSourceCode(); - // A Proxy cannot do this: oxlint defines `parserServices` as a read-only, non-configurable - // property, and a get trap may not return a different value for it. - const stubbedSourceCode = Object.create(sourceCode, { - parserServices: {value: STUB_PARSER_SERVICES}, - }); - const stubbedContext = Object.create(context, { - sourceCode: {value: stubbedSourceCode}, - getSourceCode: {value: () => stubbedSourceCode}, - languageOptions: { - value: context.languageOptions ?? {parser: {meta: {name: 'oxlint-js-plugin'}}}, - }, - }); - return rule.create(stubbedContext); - }, - }; -} +const storybook = storybookPlugin.rules; +const reactHooks = rulesOf('eslint-plugin-react-hooks'); /** * Loads `names` from an ESLint plugin as hosted rules, and makes `eslint-disable` comments that use the @@ -118,7 +75,7 @@ const plugin = { ...hostRules(importPlugin, 'import', plainlyHosted('import')), ...hostRules(jsdoc, 'jsdoc', plainlyHosted('jsdoc')), ...hostRules(storybook, 'storybook', plainlyHosted('storybook')), - ...hostRules({'naming-convention': withStubbedParserServices(typescriptEslint['naming-convention'])}, '@typescript-eslint', plainlyHosted('@typescript-eslint')), + ...hostRules({'naming-convention': namingConvention}, '@typescript-eslint', plainlyHosted('@typescript-eslint')), 'jsx-no-constructed-context-values': withFullGating(withEslintDirectiveIDs(react['jsx-no-constructed-context-values'], 'react/jsx-no-constructed-context-values')), 'no-deprecated-self-closing-jsx': withEslintDirectiveIDs({create: noDeprecatedSelfClosingJsxCreate, meta: noDeprecatedSelfClosingJsxMeta}, '@typescript-eslint/no-deprecated'), 'exhaustive-deps': withMessageGating(withEslintDirectiveIDs(reactHooks['exhaustive-deps'], 'react-hooks/exhaustive-deps'), EXHAUSTIVE_DEPS_USECALLBACK_USEMEMO_PATTERN), diff --git a/config/oxlint/preferLocaleCompareFromContext.mjs b/config/oxlint/preferLocaleCompareFromContext.mjs index a8c9badfc1a7..25d1ece7a40c 100644 --- a/config/oxlint/preferLocaleCompareFromContext.mjs +++ b/config/oxlint/preferLocaleCompareFromContext.mjs @@ -4,27 +4,7 @@ * skips that check. Only strings have `localeCompare`, so the one difference is an object with its own * `localeCompare`, which this copy also reports. */ -import {createRequire} from 'node:module'; -import path from 'node:path'; -import {fileURLToPath} from 'node:url'; - -const require = createRequire(import.meta.url); -const pluginDir = path.dirname(fileURLToPath(import.meta.url)); -const expensifyPlugin = path.resolve(pluginDir, '../../node_modules/eslint-config-expensify/eslint-plugin-expensify'); - -/** - * Loads a module from eslint-plugin-expensify, so this copy uses the same message as the original rule. - * `require` of these ESM modules returns the namespace, so unwrap `default`. - * @param {string} relative path inside eslint-plugin-expensify - * @returns {any} - */ -function load(relative) { - const module = require(path.join(expensifyPlugin, relative)); - return module?.default ?? module; -} - -const CONST = load('CONST.js'); -const {isInTestFile} = require(path.join(expensifyPlugin, 'utils/index.js')); +import {CONST, isInTestFile} from './vendor/expensify-rules.mjs'; const message = CONST.MESSAGE.PREFER_LOCALE_COMPARE_FROM_CONTEXT; diff --git a/config/oxlint/vendor/@typescript-eslint-eslint-plugin-LICENSE b/config/oxlint/vendor/@typescript-eslint-eslint-plugin-LICENSE new file mode 100644 index 000000000000..a1164108d4d6 --- /dev/null +++ b/config/oxlint/vendor/@typescript-eslint-eslint-plugin-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 typescript-eslint and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/oxlint/vendor/@typescript-eslint-utils-LICENSE b/config/oxlint/vendor/@typescript-eslint-utils-LICENSE new file mode 100644 index 000000000000..a1164108d4d6 --- /dev/null +++ b/config/oxlint/vendor/@typescript-eslint-utils-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 typescript-eslint and other contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/oxlint/vendor/README.md b/config/oxlint/vendor/README.md new file mode 100644 index 000000000000..f170e096b9fe --- /dev/null +++ b/config/oxlint/vendor/README.md @@ -0,0 +1,51 @@ +# Syntax-only hosted rules + + + +These generated modules retain the rules Oxlint cannot run natively without installing a JavaScript +TypeScript compiler. They are **rule implementations**, not compiler or parser API replacements. +`build.mjs` checks the source versions and rejects generated imports of compiler-dependent packages. +The committed modules run directly; neither installation nor linting rebuilds them. + +| npm source | Version | Included code | License | +| --- | --- | --- | --- | +| `eslint-config-expensify` | `4.0.8` | Enabled custom rules, their constants and AST helpers | ISC | +| `@typescript-eslint/eslint-plugin` | `8.58.1` | `naming-convention` and its validators | MIT | +| `@typescript-eslint/utils` | `8.61.0` | Compiler-free AST utilities and rule option defaults | MIT | +| `eslint-plugin-testing-library` | `7.11.0` | The eight enabled rules and their detection helpers | MIT | +| `eslint-plugin-storybook` | `10.6.2` | Storybook rule implementations | MIT | + +The MIT notices are beside these modules. `eslint-config-expensify` is authored by +Florian Weber (`florian@expensify.com`) and Expensify and declares the ISC license: + +> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee +> is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. +> +> THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE +> INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +> FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +> OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +> OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +## Deliberate adaptations + +- Expensify's disabled `boolean-conditional-rendering` and `prefer-at` rules are not loaded. The existing + `unicorn/prefer-at` rule and `preferLocaleCompareFromContext.mjs` implementation remain configured. +- The naming rule uses Babel's Unicode identifier validation instead of compiler scanner helpers. + App's configuration has no type or unused modifiers. Those selectors fail explicitly if introduced; + they are not silently ignored. All other naming checks and diagnostic messages are retained. +- Testing Library and Storybook use the same compiler-free AST predicates, scope utilities, rule + defaults and ESLint utility functions, without importing the compiler-dependent utilities entry point. + +## Rebuilding + +Use `npm pack @` for the five source packages above and unpack each tarball into a +temporary directory laid out like `node_modules` (including the `@typescript-eslint` scope). +No install or peer dependency override is needed in that source directory. Then, from App: + +```sh +node config/oxlint/vendor/build.mjs /absolute/path/to/unpacked-packages +``` + +Review the generated diff and rerun the existing custom-rule tests and the full lint diagnostic +comparison before updating a source version. Keep the generated files and their license notices together. diff --git a/config/oxlint/vendor/build.mjs b/config/oxlint/vendor/build.mjs new file mode 100644 index 000000000000..8594fe513a1d --- /dev/null +++ b/config/oxlint/vendor/build.mjs @@ -0,0 +1,140 @@ +/** Rebuilds the syntax-only hosted rules from unpacked npm packages, without installing a compiler. */ +import fs from 'node:fs'; +import path from 'node:path'; +import {fileURLToPath} from 'node:url'; +import {build} from 'esbuild'; + +const directory = path.dirname(fileURLToPath(import.meta.url)); +const sourceRoot = path.resolve(process.argv[2] ?? 'node_modules'); +const getPackagePath = (name, relative) => path.join(sourceRoot, name, relative); +const quote = JSON.stringify; +const versions = { + 'eslint-config-expensify': '4.0.8', + '@typescript-eslint/eslint-plugin': '8.58.1', + '@typescript-eslint/utils': '8.61.0', + 'eslint-plugin-testing-library': '7.11.0', + 'eslint-plugin-storybook': '10.6.2', +}; +for (const [name, version] of Object.entries(versions)) { + const actual = JSON.parse(fs.readFileSync(getPackagePath(name, 'package.json'), 'utf8')).version; + if (actual !== version) { + throw new Error(`Expected ${name}@${version}, found ${actual}. Review rule changes before updating a source version.`); + } + if (name !== 'eslint-config-expensify') { + fs.copyFileSync(getPackagePath(name, 'LICENSE'), path.join(directory, `${name.replaceAll('/', '-')}-LICENSE`)); + } +} +const pluginPath = getPackagePath('eslint-config-expensify', 'eslint-plugin-expensify'); +const config = fs.readFileSync(path.join(directory, '../../../oxlint.config.mts'), 'utf8'); +const names = [...config.matchAll(/'rulesdir\/([^']+)':\s*(?:\[\s*)?['"](?:error|warn)['"]/g)] + .map((match) => match[1]) + .filter((name) => name !== 'prefer-locale-compare-from-context') + .filter((name) => fs.existsSync(path.join(pluginPath, `${name}.js`))); +const testingRules = [...config.matchAll(/'testing-library\/([^']+)':\s*'error'/g)].map((match) => match[1]); + +const syntaxUtilities = ` +export {findVariable, isParenthesized, isOpeningParenToken, isClosingParenToken} from '@eslint-community/eslint-utils'; +import * as ASTUtils from ${quote(getPackagePath('@typescript-eslint/utils', 'dist/ast-utils/index.js'))}; +import {AST_NODE_TYPES, AST_TOKEN_TYPES} from '@typescript-eslint/types'; +import * as Scope from '@typescript-eslint/scope-manager'; +import {RuleCreator} from ${quote(getPackagePath('@typescript-eslint/utils', 'dist/eslint-utils/RuleCreator.js'))}; +const ESLintUtils = {RuleCreator}; +const TSESLint = {Scope}; +export {ASTUtils, AST_NODE_TYPES, AST_TOKEN_TYPES, ESLintUtils, RuleCreator, TSESLint}; +`; + +const namingUtilities = ` +import {ESLintUtils} from '@typescript-eslint/utils'; +import {isIdentifierName} from '@babel/helper-validator-identifier'; +const {RuleCreator} = ESLintUtils; +const createRule = RuleCreator((name) => 'https://typescript-eslint.io/rules/' + name); +const getEnumNames = (values) => Object.keys(values).filter((key) => !/^\\d+$/.test(key)); +const requiresQuoting = (name) => !isIdentifierName(name); +function getParserServices(_context, allowWithoutFullTypeInformation) { + if (!allowWithoutFullTypeInformation) { + throw new Error('Type-based naming selectors require a type-aware rule.'); + } + return {}; +} +export {createRule, getEnumNames, requiresQuoting, getParserServices}; +`; + +const syntaxOnly = { + name: 'syntax-only-rule-utilities', + setup(builder) { + builder.onResolve({filter: /^@typescript-eslint\/utils(?:\/(?:ast-utils|eslint-utils))?$/}, () => ({path: './syntax-rule-utilities.mjs', external: true})); + builder.onResolve({filter: /^(?:\.\.\/util|\.\.\/\.\.\/util)$/}, (args) => { + if (!args.importer.includes(`${path.sep}naming-convention`)) { + return; + } + return {path: 'naming-utilities', namespace: 'syntax-only'}; + }); + builder.onLoad({filter: /.*/, namespace: 'syntax-only'}, (args) => ({ + contents: namingUtilities, + resolveDir: sourceRoot, + loader: 'js', + })); + builder.onLoad({filter: /[/\\]naming-convention\.js$/}, (args) => { + const source = fs.readFileSync(args.path, 'utf8'); + const original = 'const { unusedVariables } = (0, util_1.collectVariables)(context);'; + if (!source.includes(original)) { + throw new Error('The naming rule changed; review its syntax-only adaptation.'); + } + // App has no unused or type selectors. Reject either rather than silently changing their meaning. + const replacement = ` + if (context.options.some((option) => option.types?.length || option.modifiers?.includes('unused'))) { + throw new Error('Type and unused naming selectors are not supported by this syntax-only rule.'); + } + const unusedVariables = new Set();`; + return {contents: source.replace(original, replacement), loader: 'js', resolveDir: path.dirname(args.path)}; + }); + }, +}; + +const entries = { + 'syntax-rule-utilities': syntaxUtilities, + 'expensify-rules': ` + ${names.map((name, index) => `import * as rule${index} from ${quote(path.join(pluginPath, `${name}.js`))};`).join('\n')} + import CONST from ${quote(path.join(pluginPath, 'CONST.js'))}; + import {isInTestFile} from ${quote(path.join(pluginPath, 'utils/index.js'))}; + const rules = {${names.map((name, index) => `${quote(name)}: rule${index}`).join(',')}}; + export {rules, CONST, isInTestFile}; + `, + 'naming-convention': ` + import rule from ${quote(getPackagePath('@typescript-eslint/eslint-plugin', 'dist/rules/naming-convention.js'))}; + export default rule.default; + `, + 'testing-library': ` + ${testingRules.map((name, index) => `import rule${index} from ${quote(getPackagePath('eslint-plugin-testing-library', `dist/rules/${name}.js`))};`).join('\n')} + export default {rules: {${testingRules.map((name, index) => `${quote(name)}: rule${index}.default`).join(',')}}}; + `, + storybook: `export {default} from ${quote(getPackagePath('eslint-plugin-storybook', 'dist/index.js'))};`, +}; + +for (const [name, contents] of Object.entries(entries)) { + const result = await build({ + stdin: {contents, resolveDir: sourceRoot, sourcefile: `${name}-entry.mjs`, loader: 'js'}, + outfile: path.join(directory, `${name}.mjs`), + write: false, + bundle: true, + absWorkingDir: sourceRoot, + preserveSymlinks: true, + platform: 'node', + format: 'esm', + packages: 'external', + plugins: [syntaxOnly], + legalComments: 'inline', + metafile: true, + banner: {js: `/** Generated by build.mjs. See README.md for source versions, licenses and supported naming selectors. */\nimport {createRequire as createVendorRequire} from 'node:module';\nconst require = createVendorRequire(import.meta.url);`}, + }); + for (const output of Object.values(result.metafile.outputs)) { + for (const dependency of output.imports) { + if (/^(?:typescript$|typescript-eslint$|@typescript\/|@typescript-eslint\/(?!types$|scope-manager$|visitor-keys$))/.test(dependency.path)) { + throw new Error(`The generated ${name} rule imports a compiler dependency: ${dependency.path}`); + } + } + } + for (const output of result.outputFiles) { + fs.writeFileSync(output.path, output.contents); + } +} diff --git a/config/oxlint/vendor/eslint-plugin-storybook-LICENSE b/config/oxlint/vendor/eslint-plugin-storybook-LICENSE new file mode 100644 index 000000000000..fef93981b767 --- /dev/null +++ b/config/oxlint/vendor/eslint-plugin-storybook-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Storybook contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/oxlint/vendor/eslint-plugin-testing-library-LICENSE b/config/oxlint/vendor/eslint-plugin-testing-library-LICENSE new file mode 100644 index 000000000000..c9810f7d18aa --- /dev/null +++ b/config/oxlint/vendor/eslint-plugin-testing-library-LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Mario Beltrán Alarcón + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/config/oxlint/vendor/expensify-rules.mjs b/config/oxlint/vendor/expensify-rules.mjs new file mode 100644 index 000000000000..54a6f70a7fee --- /dev/null +++ b/config/oxlint/vendor/expensify-rules.mjs @@ -0,0 +1,2464 @@ +/** Generated by build.mjs. See README.md for source versions, licenses and supported naming selectors. */ +import {createRequire as createVendorRequire} from 'node:module'; +const require = createVendorRequire(import.meta.url); +var __defProp = Object.defineProperty; +var __export = (target, all) => { + for (var name6 in all) + __defProp(target, name6, { get: all[name6], enumerable: true }); +}; + +// eslint-config-expensify/eslint-plugin-expensify/context-provider-split-values.js +var context_provider_split_values_exports = {}; +__export(context_provider_split_values_exports, { + create: () => create, + meta: () => meta, + name: () => name +}); + +// eslint-config-expensify/eslint-plugin-expensify/CONST.js +var CONST = { + MESSAGE: { + DISPLAY_NAME_PROPERTY_CLASS: "Class components must not set the displayName property.", + DISPLAY_NAME_PROPERTY_FUNCTION: "Function components must set the displayName property.", + NO_API_IN_VIEWS: "Do not call API directly outside of actions methods. Only actions should make API requests.", + NO_INLINE_NAMED_EXPORT: "Do not inline named exports.", + NO_NEGATED_VARIABLES: "Do not use negated variable names.", + NO_ONYX_CONNECT: "Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function.", + NO_THENABLE_ACTIONS_IN_VIEWS: "Calling .then() on action method {{method}} is forbidden in React views. Relocate this logic into the actions file and pass values via Onyx.", + NO_USELESS_COMPOSE: "compose() is not necessary when passed a single argument", + NO_MULTIPLE_ONYX_IN_FILE: "Only use withOnyx() once. Read here about how to properly work with dependent Onyx keys: https://github.com/Expensify/react-native-onyx#dependent-onyx-keys-and-withonyx", + PREFER_ACTIONS_SET_DATA: "Only actions should directly set or modify Onyx data. Please move this logic into a suitable action.", + PREFER_EARLY_RETURN: "Prefer an early return to a conditionally-wrapped function body", + PREFER_IMPORT_MODULE_CONTENTS: "Do not import individual exports from local modules. Prefer 'import * as' syntax.", + PREFER_ONYX_CONNECT_IN_LIBS: "Only call Onyx.connect() from inside a /src/libs/** file. React components and non-library code should not use Onyx.connect()", + PREFER_UNDERSCORE_METHOD: "Prefer '_.{{method}}' over the native function.", + PREFER_STR_METHOD: "Prefer 'Str.{{method}}' over the native function.", + PREFER_LOCALIZATION: "Use Localize.translateLocal('i18n.key') over hard coded messages.", + NO_MULTIPLE_API_CALLS: "Do not call API multiple times in the same method. The API response should return all the necessary data in a single request, and API calls should not be chained together.", + NO_CALL_ACTIONS_FROM_ACTIONS: "Calling actions from inside other actions is forbidden. If an action needs to call another action combine the two actions into a singular API call instead.", + NO_API_SIDE_EFFECTS_METHOD: "Do not use makeRequestWithSideEffects.", + PROP_TYPE_REQUIRED_FALSE: "Prop {{key}} should not be explicitly required.", + PROP_TYPE_NOT_DECLARED: "Prop {{key}} must be declared in propTypes.", + PROP_DEEFAULT_NOT_DECLARED: "Prop {{key}} must have a default value.", + HAVE_PROP_TYPES: "Component must have prop types declared.", + HAVE_DEFAULT_PROPS: "Component must have default prop values.", + ONYX_ONE_PARAM: "The withOnyx HOC must be passed at least one argument.", + MUST_USE_VARIABLE_FOR_ASSIGNMENT: "{{key}} must be assigned as a variable instead of direct assignment.", + NO_DEFAULT_PROPS: "defaultProps should not be used in function components. Use default Arguments instead.", + USE_PERIODS_ERROR_MESSAGES: "Use periods at the end of error messages.", + USE_DOUBLE_NEGATION_INSTEAD_OF_BOOLEAN: "Use !! instead of Boolean().", + NO_ACC_SPREAD_IN_REDUCE: "Avoid a use of spread (`...`) operator on accumulators in reduce callback. Mutate them directly instead.", + PREFER_TYPE_FEST_TUPLE_TO_UNION: "Prefer using `TupleToUnion` from `type-fest` for converting tuple types to union types.", + PREFER_TYPE_FEST_VALUE_OF: "Prefer using `ValueOf` from `type-fest` to extract the type of the properties of an object.", + PREFER_AT: "Prefer using the `.at()` method for array element access.", + PREFER_SHOULD_USE_NARROW_LAYOUT_INSTEAD_OF_IS_SMALL_SCREEN_WIDTH: "Prefer using `shouldUseNarrowLayout` instead of `isSmallScreenWidth` from `useResponsiveLayout`.", + PREFER_LOCALE_COMPARE_FROM_CONTEXT: "Prefer using `localeCompare` from `useLocalize` for performance.", + NO_USE_STATE_INITIALIZER_CALL_FUNCTION: "Avoid calling a function directly in the useState initializer. Use an initializer function instead (a callback).", + NO_OBJECT_KEYS_INCLUDES: "Avoid Object.keys({{object}}).includes({{key}}) for O(n) complexity. Use {{key}} in {{object}} or !!{{object}}[{{key}}] for O(1) complexity.", + PREFER_NARROW_HOOK_DEPENDENCIES: 'Dependency "{{dependency}}" is too broad. Use specific properties instead: {{properties}}', + CONTEXT_MIXES_DATA_AND_FUNCTIONS: "Context `{{contextName}}` mixes data ({{nonFunctionProps}}) and functions ({{functionProps}}). Use separate context providers for state (data) and actions (functions)." + } +}; +var CONST_default = CONST; + +// eslint-config-expensify/eslint-plugin-expensify/context-provider-split-values.js +var name = "context-provider-split-values"; +var meta = { + type: "suggestion", + docs: { + description: "Enforce that React Context Providers do not mix data (state) and functions (actions) in the same context value. Use separate context providers when you have both.", + recommended: "error" + }, + schema: [], + messages: { + contextMixesDataAndFunctions: CONST_default.MESSAGE.CONTEXT_MIXES_DATA_AND_FUNCTIONS + } +}; +function getJSXElementName(node) { + if (!node || !node.name) { + return null; + } + if (node.name.type === "JSXMemberExpression") { + const parts = []; + let current = node.name; + while (current) { + if (current.type === "JSXMemberExpression") { + if (current.property && current.property.name) { + parts.unshift(current.property.name); + } + current = current.object; + } else if (current.type === "JSXIdentifier") { + parts.unshift(current.name); + break; + } else { + break; + } + } + return parts.join("."); + } + if (node.name.type === "JSXIdentifier") { + return node.name.name; + } + return null; +} +function getValueProp(attributes) { + if (!attributes) { + return null; + } + for (let i = 0; i < attributes.length; i++) { + const a = attributes[i]; + if (a.type === "JSXAttribute" && a.name && a.name.name === "value") { + if (!a.value) { + return null; + } + if (a.value.type === "JSXExpressionContainer") { + return a.value.expression; + } + return a.value; + } + } + return null; +} +function getScopeForNode(context, node) { + const sourceCode = context.sourceCode || context.getSourceCode(); + return sourceCode.getScope(node); +} +function findScopeVariable(context, scopeNode, identifierName) { + let scope = getScopeForNode(context, scopeNode); + while (scope) { + for (let i = 0; i < scope.variables.length; i++) { + if (scope.variables[i].name === identifierName) { + return scope.variables[i]; + } + } + scope = scope.upper; + } + return null; +} +function findVariableInit(context, scopeNode, identifierName) { + const variable = findScopeVariable(context, scopeNode, identifierName); + if (!variable || !variable.defs || !variable.defs.length) { + return null; + } + const def = variable.defs[0]; + if (def.type !== "Variable" || !def.node || def.node.type !== "VariableDeclarator") { + return null; + } + return def.node.init || null; +} +function isHookCall(node, hookName) { + if (!node || node.type !== "CallExpression") { + return false; + } + const callee = node.callee; + if (callee.type === "Identifier" && callee.name === hookName) { + return true; + } + return callee.type === "MemberExpression" && callee.property && callee.property.type === "Identifier" && callee.property.name === hookName; +} +function isCreateContextCall(node) { + if (!node || node.type !== "CallExpression") { + return false; + } + const callee = node.callee; + if (callee.type === "Identifier" && callee.name === "createContext") { + return true; + } + return callee.type === "MemberExpression" && callee.property && callee.property.type === "Identifier" && callee.property.name === "createContext"; +} +function isContextProvider(node, ruleContext) { + const elementName = getJSXElementName(node); + if (!elementName) { + return false; + } + if (elementName.endsWith(".Provider")) { + return true; + } + if (node.name && node.name.type === "JSXIdentifier") { + const identName = node.name.name; + if (identName[0] && identName[0] === identName[0].toLowerCase()) { + return false; + } + const init = findVariableInit(ruleContext, node, identName); + if (init && isCreateContextCall(init)) { + return true; + } + } + return false; +} +function isHookDestructuredAtIndex(variable, hookName, index) { + if (!variable || !variable.defs || !variable.defs.length) { + return false; + } + const def = variable.defs[0]; + if (def.type !== "Variable" || !def.node || def.node.type !== "VariableDeclarator") { + return false; + } + if (!isHookCall(def.node.init, hookName)) { + return false; + } + const pattern = def.node.id; + if (!pattern || pattern.type !== "ArrayPattern") { + return false; + } + return pattern.elements.indexOf(def.name) === index; +} +function isUseStateSetter(variable) { + return isHookDestructuredAtIndex(variable, "useState", 1); +} +function isUseStateValue(variable) { + return isHookDestructuredAtIndex(variable, "useState", 0); +} +function isUseReducerDispatch(variable) { + return isHookDestructuredAtIndex(variable, "useReducer", 1); +} +function isUseReducerState(variable) { + return isHookDestructuredAtIndex(variable, "useReducer", 0); +} +function isFunctionNode(node) { + return node && (node.type === "ArrowFunctionExpression" || node.type === "FunctionExpression"); +} +function getFactoryReturnNode(factoryNode) { + if (!factoryNode || !isFunctionNode(factoryNode)) { + return null; + } + if (factoryNode.body.type !== "BlockStatement") { + return factoryNode.body; + } + const stmts = factoryNode.body.body; + for (let i = 0; i < stmts.length; i++) { + if (stmts[i].type === "ReturnStatement" && stmts[i].argument) { + return stmts[i].argument; + } + } + return null; +} +function unwrapTSNode(node) { + if (!node) { + return node; + } + if (node.type === "TSAsExpression" || node.type === "TSSatisfiesExpression" || node.type === "TSNonNullExpression" || node.type === "TSTypeAssertion") { + return unwrapTSNode(node.expression); + } + return node; +} +function classifyValue(node, context, scopeNode, visited) { + const seen = visited || /* @__PURE__ */ new Set(); + const unwrapped = unwrapTSNode(node); + if (!unwrapped) { + return null; + } + if (isFunctionNode(unwrapped)) { + return "function"; + } + if (unwrapped.type === "Literal" || unwrapped.type === "TemplateLiteral" || unwrapped.type === "ArrayExpression" || unwrapped.type === "ObjectExpression") { + return "data"; + } + if (unwrapped.type === "UnaryExpression" || unwrapped.type === "BinaryExpression" || unwrapped.type === "UpdateExpression" || unwrapped.type === "NewExpression") { + return "data"; + } + if (unwrapped.type === "ConditionalExpression") { + const cons = classifyValue(unwrapped.consequent, context, scopeNode, seen); + const alt = classifyValue(unwrapped.alternate, context, scopeNode, seen); + if (cons && cons === alt) { + return cons; + } + return null; + } + if (unwrapped.type === "LogicalExpression") { + const left = classifyValue(unwrapped.left, context, scopeNode, seen); + const right = classifyValue(unwrapped.right, context, scopeNode, seen); + if (left && left === right) { + return left; + } + return null; + } + if (unwrapped.type === "CallExpression") { + if (isHookCall(unwrapped, "useCallback")) { + return "function"; + } + if (isHookCall(unwrapped, "useMemo")) { + const factory = unwrapped.arguments[0]; + const returnNode = getFactoryReturnNode(factory); + if (returnNode) { + return classifyValue(returnNode, context, scopeNode, seen); + } + return null; + } + if (isHookCall(unwrapped, "useRef")) { + return "data"; + } + return null; + } + if (unwrapped.type === "Identifier") { + if (seen.has(unwrapped.name)) { + return null; + } + seen.add(unwrapped.name); + const variable = findScopeVariable(context, scopeNode, unwrapped.name); + if (variable) { + if (isUseStateSetter(variable) || isUseReducerDispatch(variable)) { + return "function"; + } + if (isUseStateValue(variable) || isUseReducerState(variable)) { + return "data"; + } + if (variable.defs && variable.defs.length && variable.defs[0].type === "FunctionName") { + return "function"; + } + } + const init = findVariableInit(context, scopeNode, unwrapped.name); + if (init) { + return classifyValue(init, context, scopeNode, seen); + } + return null; + } + return null; +} +function resolveToObject(node, context, scopeNode) { + const unwrapped = unwrapTSNode(node); + if (!unwrapped) { + return null; + } + if (unwrapped.type === "ObjectExpression") { + return unwrapped; + } + if (unwrapped.type === "Identifier") { + const init = findVariableInit(context, scopeNode, unwrapped.name); + if (init) { + return resolveToObject(init, context, scopeNode); + } + return null; + } + if (unwrapped.type === "CallExpression" && isHookCall(unwrapped, "useMemo")) { + const factory = unwrapped.arguments[0]; + const returnNode = getFactoryReturnNode(factory); + if (returnNode) { + return resolveToObject(returnNode, context, scopeNode); + } + return null; + } + return null; +} +function analyzeObjectProperties(objectNode, context, scopeNode, visited) { + const seen = visited || /* @__PURE__ */ new Set(); + const result = { + hasFunctions: false, + hasNonFunctions: false, + functionProps: [], + nonFunctionProps: [] + }; + if (!objectNode || objectNode.type !== "ObjectExpression") { + return result; + } + if (seen.has(objectNode)) { + return result; + } + seen.add(objectNode); + for (let i = 0; i < objectNode.properties.length; i++) { + const prop = objectNode.properties[i]; + if (prop.type === "SpreadElement") { + let spreadObj = prop.argument; + if (spreadObj && spreadObj.type === "Identifier") { + const resolved = resolveToObject(spreadObj, context, scopeNode); + if (resolved) { + spreadObj = resolved; + } + } + if (spreadObj && spreadObj.type === "ObjectExpression") { + const sub = analyzeObjectProperties(spreadObj, context, scopeNode, seen); + if (sub.hasFunctions) { + result.hasFunctions = true; + result.functionProps.push(...sub.functionProps); + } + if (sub.hasNonFunctions) { + result.hasNonFunctions = true; + result.nonFunctionProps.push(...sub.nonFunctionProps); + } + } + continue; + } + const propName = prop.key && (prop.key.name || prop.key.value) || "unknown"; + const classification = classifyValue(prop.value, context, scopeNode); + if (classification === "function") { + result.hasFunctions = true; + result.functionProps.push(propName); + } else if (classification === "data") { + result.hasNonFunctions = true; + result.nonFunctionProps.push(propName); + } + } + return result; +} +function create(context) { + return { + JSXOpeningElement(node) { + const valueProp = getValueProp(node.attributes); + if (!valueProp) { + return; + } + if (!isContextProvider(node, context)) { + return; + } + const objectToAnalyze = resolveToObject(valueProp, context, node); + if (!objectToAnalyze) { + return; + } + const analysis = analyzeObjectProperties(objectToAnalyze, context, node); + if (analysis.hasFunctions && analysis.hasNonFunctions) { + context.report({ + node, + messageId: "contextMixesDataAndFunctions", + data: { + contextName: getJSXElementName(node), + functionProps: analysis.functionProps.join(", "), + nonFunctionProps: analysis.nonFunctionProps.join(", ") + } + }); + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-acc-spread-in-reduce.js +var no_acc_spread_in_reduce_exports = {}; +__export(no_acc_spread_in_reduce_exports, { + create: () => create2, + meta: () => meta2 +}); +import _ from "lodash"; +var MATCH = 'CallExpression:matches([callee.property.name="reduce"], [callee.property.name="reduceRight"]) > :matches(ArrowFunctionExpression, FunctionExpression)'; +var meta2 = { + type: "problem", + docs: { + description: CONST_default.MESSAGE.NO_ACC_SPREAD_IN_REDUCE, + category: "Best Practices", + recommended: false + }, + schema: [] + // no options +}; +function create2(context) { + const sourceCode = context.sourceCode; + return { + [MATCH](node) { + const accumulator = sourceCode.getDeclaredVariables(node)[0]; + if (!accumulator) { + return; + } + if (!accumulator.references.length) { + return; + } + const isAccumulatorVariableUsedSpread = _.some( + accumulator.references, + (reference) => reference.identifier.parent.type === "SpreadElement" + ); + if (!isAccumulatorVariableUsedSpread) { + return; + } + context.report({ + node, + message: CONST_default.MESSAGE.NO_ACC_SPREAD_IN_REDUCE + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-api-in-views.js +var no_api_in_views_exports = {}; +__export(no_api_in_views_exports, { + create: () => create3 +}); + +// eslint-config-expensify/eslint-plugin-expensify/utils/index.js +import lodashGet from "lodash/get.js"; +function isReactViewFile(filename) { + return filename.includes("/src/pages/") || filename.includes("/src/components"); +} +function isOnyxMethodCall(node) { + return lodashGet(node, "object.name") === "Onyx"; +} +function isInActionFile(filename) { + return filename.includes("/src/libs/actions/"); +} +function isInTestFile(filename) { + return filename.includes("/tests/"); +} + +// eslint-config-expensify/eslint-plugin-expensify/no-api-in-views.js +var message = CONST_default.MESSAGE.NO_API_IN_VIEWS; +function create3(context) { + return { + Identifier(node) { + if (isInActionFile(context.getFilename())) { + return; + } + if (isInTestFile(context.getFilename())) { + return; + } + if (node.name !== "API" && node.name !== "DeprecatedAPI") { + return; + } + context.report({ + node, + message + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-api-side-effects-method.js +var no_api_side_effects_method_exports = {}; +__export(no_api_side_effects_method_exports, { + create: () => create4 +}); +import lodashGet2 from "lodash/get.js"; +var message2 = CONST_default.MESSAGE.NO_API_SIDE_EFFECTS_METHOD; +function create4(context) { + return { + CallExpression(node) { + const name6 = lodashGet2(node, "callee.property.name"); + if (!name6) { + return; + } + if (name6 !== "makeRequestWithSideEffects") { + return; + } + context.report({ + node, + message: message2 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-beta-handler.js +var no_beta_handler_exports = {}; +__export(no_beta_handler_exports, { + create: () => create5, + meta: () => meta3 +}); +var meta3 = { + type: "problem", + docs: { + description: "Disallow custom functions that check for beta features using `betas?.includes(CONST.BETAS.BETA_NAME)`. Suggests using `isBetaEnabled` instead." + }, + fixable: null, + schema: [], + messages: { + useIsBetaEnabled: "Avoid creating custom beta checker functions (e.g., '{{ functionName }}'). Use `isBetaEnabled` utility method instead for checking beta features." + } +}; +function create5(context) { + function isBetaIncludesCall(node) { + if (node.type !== "CallExpression") { + return false; + } + const callee = node.callee; + if (!(callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === "includes")) { + return false; + } + if (!(callee.object.type === "Identifier" && callee.object.name === "betas")) { + return false; + } + if (node.arguments.length === 1) { + return true; + } + return false; + } + function containsBetaCheck(node) { + if (!node) { + return false; + } + if (node.type === "UnaryExpression") { + return containsBetaCheck(node.argument); + } + if (node.type === "ChainExpression") { + return containsBetaCheck(node.expression); + } + if (isBetaIncludesCall(node)) { + return true; + } + if (node.type === "LogicalExpression") { + return containsBetaCheck(node.left) || containsBetaCheck(node.right); + } + if (node.type === "ConditionalExpression") { + return containsBetaCheck(node.test) || containsBetaCheck(node.consequent) || containsBetaCheck(node.alternate); + } + return false; + } + function reportFunction(node, functionName) { + context.report({ + node, + messageId: "useIsBetaEnabled", + data: { functionName: functionName || "anonymous function" } + }); + } + return { + // Visit FunctionDeclaration nodes (e.g., `function canUseAutoSubmit(...) { ... }`) + FunctionDeclaration(node) { + if (node.id && node.id.name === "isBetaEnabled" || !node.body || node.body.type !== "BlockStatement") { + return; + } + for (const statement of node.body.body) { + if (statement.type === "ReturnStatement") { + if (containsBetaCheck(statement.argument)) { + reportFunction(node, node.id ? node.id.name : ""); + break; + } + } + } + }, + // Visit VariableDeclarator nodes (e.g., `const canUseAutoSubmit = (betas) => { ... };`) + VariableDeclarator(node) { + if (node.id && node.id.name === "isBetaEnabled" || !node.init || !(node.init.type === "ArrowFunctionExpression" || node.init.type === "FunctionExpression")) { + return; + } + const funcBody = node.init.body; + if (funcBody.type === "BlockStatement") { + for (const statement of funcBody.body) { + if (statement.type === "ReturnStatement") { + if (containsBetaCheck(statement.argument)) { + reportFunction(node, node.id ? node.id.name : ""); + break; + } + } + } + } else { + if (!containsBetaCheck(funcBody)) { + return; + } + reportFunction(node, node.id ? node.id.name : ""); + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-call-actions-from-actions.js +var no_call_actions_from_actions_exports = {}; +__export(no_call_actions_from_actions_exports, { + create: () => create6 +}); +import _2 from "underscore"; +import lodashGet3 from "lodash/get.js"; +import path from "path"; +var message3 = CONST_default.MESSAGE.NO_CALL_ACTIONS_FROM_ACTIONS; +function create6(context) { + const actions = []; + function hasActionCall(tokens) { + return _2.find(tokens, (token) => _2.includes(actions, token.value)); + } + function hasAPICall(tokens) { + return _2.find(tokens, (token) => token.value === "API"); + } + function checkFunctionBody(node) { + if (!isInActionFile(context.getFilename())) { + return; + } + const tokens = context.getSourceCode().getTokens(node); + if (hasAPICall(tokens) && hasActionCall(tokens)) { + context.report({ + node, + message: message3 + }); + } + } + return { + ImportDeclaration(node) { + const pathName = path.resolve(lodashGet3(node, "source.value")); + if (!pathName || !pathName.includes("/actions/")) { + return; + } + const filename = _2.last(pathName.split("/")); + actions.push(_2.first(filename.split("."))); + }, + FunctionDeclaration: checkFunctionBody, + FunctionExpression: checkFunctionBody, + ArrowFunctionExpression: checkFunctionBody + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-deep-equal-in-memo.js +var no_deep_equal_in_memo_exports = {}; +__export(no_deep_equal_in_memo_exports, { + create: () => create7, + meta: () => meta4, + name: () => name2 +}); +import _3 from "lodash"; +var name2 = "no-deep-equal-in-memo"; +var meta4 = { + type: "problem", + docs: { + description: "Disallow deep equality checks in React.memo comparison functions. Use shallow comparisons of specific properties instead.", + recommended: "error" + }, + schema: [], + messages: { + noDeepEqualInMemo: "Avoid using deep equality checks ({{functionName}}) in React.memo. Compare specific relevant properties with shallow equality instead for better performance." + } +}; +var DEEP_COMPARISON_FUNCTIONS = /* @__PURE__ */ new Set([ + "deepEqual", + "isEqual" +]); +function isDeepComparisonCall(node) { + if (node.type !== "CallExpression") { + return { isDeepComparison: false, functionName: null }; + } + if (node.callee.type === "Identifier" && DEEP_COMPARISON_FUNCTIONS.has(node.callee.name)) { + return { isDeepComparison: true, functionName: node.callee.name }; + } + if (node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "_" && node.callee.property.type === "Identifier" && DEEP_COMPARISON_FUNCTIONS.has(node.callee.property.name)) { + return { isDeepComparison: true, functionName: `_.${node.callee.property.name}` }; + } + return { isDeepComparison: false, functionName: null }; +} +function isReactMemoCall(node) { + if (node.type !== "CallExpression") { + return false; + } + if (node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "React" && node.callee.property.type === "Identifier" && node.callee.property.name === "memo") { + return true; + } + if (node.callee.type === "Identifier" && node.callee.name === "memo") { + return true; + } + return false; +} +function traverseForDeepComparisons(node, callback, visited = /* @__PURE__ */ new Set()) { + if (!node || !node.type || visited.has(node)) { + return; + } + visited.add(node); + const { isDeepComparison, functionName } = isDeepComparisonCall(node); + if (isDeepComparison) { + callback(node, functionName); + } + for (const key of _3.keys(node)) { + if (key === "parent" || key === "type" || key === "range" || key === "loc") { + continue; + } + const value = node[key]; + if (_3.isArray(value)) { + for (const item of value) { + if (item && typeof item === "object" && item.type) { + traverseForDeepComparisons(item, callback, visited); + } + } + } else if (value && typeof value === "object" && value.type) { + traverseForDeepComparisons(value, callback, visited); + } + } +} +function create7(context) { + return { + CallExpression(node) { + if (!isReactMemoCall(node)) { + return; + } + if (node.arguments.length < 2) { + return; + } + const comparisonFunction = node.arguments[1]; + if (comparisonFunction.type !== "FunctionExpression" && comparisonFunction.type !== "ArrowFunctionExpression") { + return; + } + const visited = /* @__PURE__ */ new Set(); + traverseForDeepComparisons( + comparisonFunction.body, + (deepComparisonNode, functionName) => { + context.report({ + node: deepComparisonNode, + messageId: "noDeepEqualInMemo", + data: { + functionName + } + }); + }, + visited + ); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-default-id-values.js +var no_default_id_values_exports = {}; +__export(no_default_id_values_exports, { + create: () => create8, + meta: () => meta5, + name: () => name3 +}); +function createPatternRegex(pattern) { + return new RegExp(pattern.replaceAll(/([.?*+^$[\]\\(){}|-])/g, "\\$1"), "g"); +} +function searchForPatternsAndReport(context, sourceCode, soureCodeStr, pattern, messageId) { + const regex = createPatternRegex(pattern); + let match = regex.exec(soureCodeStr); + while (match !== null) { + const index = match.index; + const defaultStr = match[0]; + const defaultStrPosition = sourceCode.getLocFromIndex(index); + context.report({ + messageId, + loc: { + start: { line: defaultStrPosition.line, column: defaultStrPosition.column + defaultStr.indexOf(" ") }, + end: { line: defaultStrPosition.line, column: defaultStrPosition.column + defaultStr.length } + } + }); + match = regex.exec(soureCodeStr); + } +} +var name3 = "no-default-id-values"; +var meta5 = { + type: "problem", + docs: { + description: "Restricts use of default number/string IDs in the project.", + recommended: "error" + }, + schema: [], + messages: { + // eslint-disable-next-line max-len + disallowedNumberDefault: "Default the number ID to `CONST.DEFAULT_NUMBER_ID` instead. See: https://github.com/Expensify/App/blob/main/contributingGuides/STYLE.md#default-value-for-inexistent-IDs", + disallowedStringDefault: "Do not default string IDs to any value. See: https://github.com/Expensify/App/blob/main/contributingGuides/STYLE.md#default-value-for-inexistent-IDs" + } +}; +function create8(context) { + const sourceCode = context.getSourceCode(); + const soureCodeStr = sourceCode.text; + const disallowedNumberDefaults = [ + "ID ?? -1", + "id ?? -1", + "ID ?? 0", + "id ?? 0", + "ID || -1", + "id || -1", + "ID || 0", + "id || 0", + "ID : -1", + "id : -1", + "ID : 0", + "id : 0" + ]; + const disallowedStringDefaults = [ + " ?? '-1'", + "ID ?? ''", + "id ?? ''", + "ID ?? '0'", + "id ?? '0'", + " || '-1'", + "ID || ''", + "id || ''", + "ID || '0'", + "id || '0'", + " : '-1'", + " : '0'", + "CONST.DEFAULT_NUMBER_ID}`" + ]; + for (const pattern of disallowedNumberDefaults) { + searchForPatternsAndReport(context, sourceCode, soureCodeStr, pattern, "disallowedNumberDefault"); + } + for (const pattern of disallowedStringDefaults) { + searchForPatternsAndReport(context, sourceCode, soureCodeStr, pattern, "disallowedStringDefault"); + } + return {}; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-default-props.js +var no_default_props_exports = {}; +__export(no_default_props_exports, { + create: () => create9 +}); +import _4 from "underscore"; +import lodashGet4 from "lodash/get.js"; +var message4 = CONST_default.MESSAGE.NO_DEFAULT_PROPS; +function create9(context) { + return { + AssignmentExpression(node) { + if (!isReactViewFile(context.getFilename())) { + return; + } + if (lodashGet4(node, "left.property.name") !== "defaultProps") { + return; + } + const parent = lodashGet4(node, "parent.type") === "FunctionDeclaration" ? node.parent : lodashGet4(node, "parent.parent"); + const functionComponents = _4.filter(parent.body, (n) => { + if (["FunctionDeclaration", "ExportNamedDeclaration"].indexOf(n.type) === -1) { + return false; + } + const body = n.type === "ExportNamedDeclaration" ? lodashGet4(n, "declaration.body.body") : lodashGet4(n, "body.body"); + const isReturningJSX = _4.filter(body, (bodyNode) => bodyNode.type === "ReturnStatement" && lodashGet4(bodyNode, "argument.type") === "JSXElement"); + if (_4.isEmpty(isReturningJSX)) { + return false; + } + return true; + }); + if (_4.isEmpty(functionComponents)) { + return; + } + const functionComponentNames = _4.map(functionComponents, (functionComponent) => { + if (functionComponent.type === "FunctionDeclaration") { + return functionComponent.id.name; + } + return lodashGet4(functionComponent, "declaration.id.name"); + }); + if (!_4.includes(functionComponentNames, lodashGet4(node, "left.object.name"))) { + return; + } + context.report({ + node, + message: message4 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-inline-named-export.js +var no_inline_named_export_exports = {}; +__export(no_inline_named_export_exports, { + create: () => create10 +}); +var message5 = CONST_default.MESSAGE.NO_INLINE_NAMED_EXPORT; +function create10(context) { + return { + ExportNamedDeclaration(node) { + if (!node.declaration) { + return; + } + context.report({ + node, + message: message5 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-multiple-api-calls.js +var no_multiple_api_calls_exports = {}; +__export(no_multiple_api_calls_exports, { + create: () => create11 +}); +import _5 from "underscore"; +var message6 = CONST_default.MESSAGE.NO_MULTIPLE_API_CALLS; +function create11(context) { + function checkFunctionBody(node) { + const tokens = context.getSourceCode().getTokens(node); + let hasCalledAPI = false; + _5.each(tokens, (token) => { + const isAPICall = token.value === "API"; + if (!isAPICall) { + return; + } + if (!hasCalledAPI) { + hasCalledAPI = true; + return; + } + context.report({ + node: token, + message: message6 + }); + }); + } + return { + FunctionDeclaration: checkFunctionBody, + FunctionExpression: checkFunctionBody, + ArrowFunctionExpression: checkFunctionBody + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-multiple-onyx-in-file.js +var no_multiple_onyx_in_file_exports = {}; +__export(no_multiple_onyx_in_file_exports, { + create: () => create12 +}); +import lodashGet5 from "lodash/get.js"; +var message7 = CONST_default.MESSAGE.NO_MULTIPLE_ONYX_IN_FILE; +function create12(context) { + let withOnyxCount = 0; + return { + CallExpression(node) { + const calleeName = lodashGet5(node, "callee.name"); + if (calleeName === "withOnyx") { + withOnyxCount += 1; + if (withOnyxCount > 1) { + context.report({ + node, + message: message7 + }); + } + } + }, + "Program:exit": () => { + withOnyxCount = 0; + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-negated-variables.js +var no_negated_variables_exports = {}; +__export(no_negated_variables_exports, { + create: () => create13 +}); +import _6 from "underscore"; +import lodashGet6 from "lodash/get.js"; +var message8 = CONST_default.MESSAGE.NO_NEGATED_VARIABLES; +var BANNED_SUBSTRINGS = [ + "not", + "isnot", + "cannot", + "shouldnot", + "cant", + "dont" +]; +var NOTABLE_EXCEPTIONS = [ + "notification", + "notify", + "notch", + "note", + "notable", + "notion", + "notice", + "notation", + "annotation", + "notFound" +]; +function camelToUpperSnake(word) { + return word.replaceAll(/([a-z])([A-Z])/g, "$1_$2").toUpperCase(); +} +function buildExceptionAlternatives(word) { + const hasInternalCap = /[a-z][A-Z]/.test(word); + const upperFirst = word[0].toUpperCase() + word.slice(1); + const lowerFirst = word[0].toLowerCase() + word.slice(1); + const snake = camelToUpperSnake(word); + if (hasInternalCap) { + return [ + `${upperFirst}(?=[A-Z_]|$)`, + `${lowerFirst}(?=[A-Z_]|$)`, + `_?${snake}(?=_|$)` + ]; + } + return [ + upperFirst, + lowerFirst, + `_?${snake}_?` + ]; +} +function isFalsePositive(string) { + const alternatives = _6.flatten(_6.map(NOTABLE_EXCEPTIONS, buildExceptionAlternatives)); + const regex = new RegExp(`(.*?)(?:${alternatives.join("|")})(.*)`, "m"); + const matches = regex.exec(string); + if (!matches) { + return false; + } + const prefix = matches[1]; + const suffix = matches[2]; + const isPrefixNegatedVariableName = isNegatedVariableName(prefix); + const isSuffixNegatedVariableName = isNegatedVariableName(suffix); + return !isPrefixNegatedVariableName && !isSuffixNegatedVariableName; +} +function isNegatedVariableName(name6) { + if (!name6) { + return false; + } + return _6.any(BANNED_SUBSTRINGS, (badSubstring) => name6.toLowerCase().includes(badSubstring)) && !isFalsePositive(name6); +} +function create13(context) { + return { + FunctionDeclaration(node) { + const name6 = lodashGet6(node, "id.name"); + if (!name6) { + return; + } + if (!isNegatedVariableName(name6)) { + return; + } + context.report({ + node, + message: message8 + }); + }, + VariableDeclarator(node) { + const name6 = lodashGet6(node, "id.name"); + if (!name6) { + return; + } + if (!isNegatedVariableName(node.id.name)) { + return; + } + context.report({ + node, + message: message8 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-onyx-connect.js +var no_onyx_connect_exports = {}; +__export(no_onyx_connect_exports, { + create: () => create14 +}); +import lodashGet7 from "lodash/get.js"; +var message9 = CONST_default.MESSAGE.NO_ONYX_CONNECT; +function create14(context) { + return { + MemberExpression(node) { + if (!isOnyxMethodCall(node)) { + return; + } + if (isInTestFile(context.getFilename())) { + return; + } + const methodName = lodashGet7(node, "property.name"); + if (methodName !== "connect") { + return; + } + context.report({ + node, + message: message9 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-set-or-map-return-in-useOnyx-selector.js +var no_set_or_map_return_in_useOnyx_selector_exports = {}; +__export(no_set_or_map_return_in_useOnyx_selector_exports, { + create: () => create15, + meta: () => meta6 +}); +import _8 from "underscore"; + +// eslint-config-expensify/eslint-plugin-expensify/utils/astUtil.js +import _7 from "lodash"; +function getMethodName(node) { + return _7.get(node, "callee.property.name"); +} +function isCallFromObject(node, objName) { + return node && node.type === "CallExpression" && _7.get(node, "callee.object.name") === objName; +} +function findProperty(objectExpression, propertyName) { + return _7.find( + objectExpression.properties, + (property) => property.type === "Property" && property.key.name === propertyName + ) || null; +} +function resolveVariable(context, varName, node) { + try { + const scope = context.sourceCode.getScope(node); + return scope.set.get(varName) || (_7.get(scope.upper, "set") || /* @__PURE__ */ new Set()).get(varName) || null; + } catch { + return null; + } +} +function getVariableInit(context, varName, node) { + const variable = resolveVariable(context, varName, node); + if (variable && variable.defs.length > 0) { + return variable.defs[0].node.init || null; + } + return null; +} +function getVariableAsObject(context, varName, node) { + const init = getVariableInit(context, varName, node); + return init && init.type === "ObjectExpression" ? init : null; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-set-or-map-return-in-useOnyx-selector.js +var meta6 = { + type: "problem", + docs: { + description: "Disallow returning Set, ReadonlySet, Map, or ReadonlyMap from useOnyx selectors.", + recommended: "error" + }, + schema: [], + messages: { + noSetOrMapReturn: "Avoid returning Sets or Maps from useOnyx selectors. The deepEqual comparison used by useOnyx is extremely slow for Set and Map objects. Return an array or plain object instead and convert outside the selector if needed." + } +}; +function create15(context) { + function resolveSelectorFunction(selectorValue, refNode) { + if (!selectorValue) { + return null; + } + if (selectorValue.type === "ArrowFunctionExpression" || selectorValue.type === "FunctionExpression") { + return selectorValue; + } + if (selectorValue.type === "Identifier") { + const init = getVariableInit(context, selectorValue.name, refNode); + if (!init) { + return null; + } + if (init.type === "ArrowFunctionExpression" || init.type === "FunctionExpression") { + return init; + } + const callArgs = _8.get(init, "arguments", []); + if (init.type === "CallExpression" && callArgs.length > 0 && init.callee && init.callee.type === "Identifier") { + const hookName = init.callee.name; + const inner = callArgs[0]; + if (hookName === "useCallback" && (inner.type === "ArrowFunctionExpression" || inner.type === "FunctionExpression")) { + return inner; + } + if (hookName === "useMemo" && (inner.type === "ArrowFunctionExpression" || inner.type === "FunctionExpression")) { + if (inner.body.type === "ArrowFunctionExpression" || inner.body.type === "FunctionExpression") { + return inner.body; + } + if (inner.body.type === "BlockStatement") { + for (const stmt of inner.body.body) { + if (stmt.type === "ReturnStatement" && stmt.argument && (stmt.argument.type === "ArrowFunctionExpression" || stmt.argument.type === "FunctionExpression")) { + return stmt.argument; + } + } + } + } + } + } + return null; + } + function isNewSetOrMapExpression(node) { + return node && node.type === "NewExpression" && node.callee && node.callee.type === "Identifier" && (node.callee.name === "Set" || node.callee.name === "ReadonlySet" || node.callee.name === "Map" || node.callee.name === "ReadonlyMap"); + } + function expressionMayReturnSetOrMap(node) { + if (!node) { + return false; + } + if (isNewSetOrMapExpression(node)) { + return true; + } + if (node.type === "ConditionalExpression") { + return isNewSetOrMapExpression(node.consequent) || isNewSetOrMapExpression(node.alternate); + } + return false; + } + function bodyReturnsNewSetOrMap(body) { + if (!body) { + return false; + } + if (expressionMayReturnSetOrMap(body)) { + return true; + } + if (body.type === "BlockStatement") { + for (const stmt of body.body) { + if (stmt.type !== "ReturnStatement" || !stmt.argument) { + continue; + } + if (expressionMayReturnSetOrMap(stmt.argument)) { + return true; + } + if (stmt.argument.type === "Identifier") { + const init = getVariableInit(context, stmt.argument.name, stmt); + if (isNewSetOrMapExpression(init)) { + return true; + } + } + } + } + return false; + } + function hasSetOrMapReturnType(funcNode) { + const returnType = _8.get(funcNode, "returnType.typeAnnotation"); + if (!returnType) { + return false; + } + if (returnType.type === "TSTypeReference") { + const typeName = _8.get(returnType, "typeName.name"); + return typeName === "Set" || typeName === "ReadonlySet" || typeName === "Map" || typeName === "ReadonlyMap"; + } + return false; + } + function checkSelectorForSetOrMap(selectorFunc, reportNode) { + if (!selectorFunc) { + return; + } + if (bodyReturnsNewSetOrMap(selectorFunc.body) || hasSetOrMapReturnType(selectorFunc)) { + context.report({ + node: reportNode, + messageId: "noSetOrMapReturn" + }); + } + } + return { + VariableDeclarator(node) { + if (!node.init || node.init.type !== "CallExpression" || node.init.callee.name !== "useOnyx") { + return; + } + if (node.init.arguments.length < 2) { + return; + } + const optionsArgument = node.init.arguments[1]; + switch (optionsArgument.type) { + case "ObjectExpression": { + const selectorProperty = findProperty(optionsArgument, "selector"); + if (selectorProperty) { + const func = resolveSelectorFunction(selectorProperty.value, node); + checkSelectorForSetOrMap(func, node.init); + } + break; + } + case "Identifier": { + const resolved = getVariableAsObject(context, optionsArgument.name, node); + if (resolved) { + const selectorProperty = findProperty(resolved, "selector"); + if (selectorProperty) { + const func = resolveSelectorFunction(selectorProperty.value, node); + checkSelectorForSetOrMap(func, node.init); + } + } + break; + } + default: { + break; + } + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-thenable-actions-in-views.js +var no_thenable_actions_in_views_exports = {}; +__export(no_thenable_actions_in_views_exports, { + create: () => create16 +}); +import _9 from "underscore"; +import lodashGet8 from "lodash/get.js"; +import path2 from "path"; +var message10 = CONST_default.MESSAGE.NO_THENABLE_ACTIONS_IN_VIEWS; +function create16(context) { + const actionsNamespaces = []; + return { + // Using import declaration to create a map of all the imports for this file and which ones are "actions" + ImportDeclaration(node) { + const pathName = path2.resolve(lodashGet8(node, "source.value")); + if (!pathName || !pathName.includes("/actions/")) { + return; + } + actionsNamespaces.push(_9.last(pathName.split("/"))); + }, + MemberExpression(node) { + if (!isReactViewFile(context.getFilename())) { + return; + } + if (lodashGet8(node, "property.name") !== "then") { + return; + } + const actionModuleName = lodashGet8(node, "object.callee.object.name"); + if (!_9.includes(actionsNamespaces, actionModuleName)) { + return; + } + const actionMethodName = lodashGet8(node, "object.callee.property.name"); + context.report({ + node, + message: message10, + data: { + method: `${actionModuleName}.${actionMethodName}()` + } + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-unstable-hook-defaults.js +var no_unstable_hook_defaults_exports = {}; +__export(no_unstable_hook_defaults_exports, { + create: () => create17, + meta: () => meta7, + name: () => name4 +}); +var name4 = "no-unstable-hook-defaults"; +var meta7 = { + type: "problem", + docs: { + description: "Disallow inline array or object literals as default values in hooks. Use getEmptyArray(), getEmptyObject(), a constant, or a memoized value instead.", + recommended: "error" + }, + schema: [], + messages: { + noEmptyArrayDefault: "Avoid using empty array [] as default value in hooks. Use getEmptyArray() instead.", + noInlineArrayDefault: "Avoid using array literal as default value in hooks. Use a memoized value or a constant instead.", + noEmptyObjectDefault: "Avoid using empty object {} as default value in hooks. Use getEmptyObject() instead.", + noInlineObjectDefault: "Avoid using object literal as default value in hooks. Use a memoized value or a constant instead.", + noUnstableIdentifierDefault: "Default value must be memoized with useMemo or declared as a top-level constant." + } +}; +function create17(context) { + const sourceCode = context.sourceCode; + function checkDefaultValue(defaultValue) { + if (defaultValue.type === "ArrayExpression") { + const isEmpty = defaultValue.elements.length === 0; + context.report({ + node: defaultValue, + messageId: isEmpty ? "noEmptyArrayDefault" : "noInlineArrayDefault" + }); + } + if (defaultValue.type === "ObjectExpression") { + const isEmpty = defaultValue.properties.length === 0; + context.report({ + node: defaultValue, + messageId: isEmpty ? "noEmptyObjectDefault" : "noInlineObjectDefault" + }); + } + if (defaultValue.type === "Identifier") { + const scope = sourceCode.getScope(defaultValue); + const variable = scope.set.get(defaultValue.name); + if (variable && variable.defs.length > 0 && variable.defs[0].node.type === "VariableDeclarator") { + const def = variable.defs[0].node; + const init = def.init; + const isMemoized = init && init.type === "CallExpression" && init.callee.type === "Identifier" && init.callee.name === "useMemo"; + const isTopLevelConst = variable.scope.type === "module" && variable.defs[0].parent.kind === "const"; + if (!isMemoized && !isTopLevelConst) { + context.report({ + node: defaultValue, + messageId: "noUnstableIdentifierDefault" + }); + } + } + } + } + return { + VariableDeclarator(node) { + if (!node.init || node.init.type !== "CallExpression" || !node.init.callee.name || !node.init.callee.name.startsWith("use")) { + return; + } + if (node.id.type === "ArrayPattern") { + for (const element of node.id.elements) { + if (!element || element.type !== "AssignmentPattern") { + continue; + } + checkDefaultValue(element.right); + } + } + if (node.id.type === "ObjectPattern") { + for (const property of node.id.properties) { + if (!property || property.type !== "Property" || property.value.type !== "AssignmentPattern") { + continue; + } + checkDefaultValue(property.value.right); + } + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-use-state-initializer-functions.js +var no_use_state_initializer_functions_exports = {}; +__export(no_use_state_initializer_functions_exports, { + create: () => create18, + meta: () => meta8 +}); +var message11 = CONST_default.MESSAGE.NO_USE_STATE_INITIALIZER_CALL_FUNCTION; +var meta8 = { + type: "problem", + // This is a potential performance issue + docs: { + description: "Disallow direct function calls in useState initializer", + category: "Best Practices", + recommended: false + }, + schema: [], + // No options for this rule + messages: { + noDirectCall: message11 + } +}; +function create18(context) { + return { + CallExpression(node) { + if (node.callee.type !== "Identifier" || node.callee.name !== "useState" || node.arguments.length === 0) { + return; + } + const firstArg = node.arguments[0]; + if (firstArg.type !== "CallExpression" && !(firstArg.type === "MemberExpression" && firstArg.object.type === "CallExpression") && !(firstArg.type === "ConditionalExpression" && (firstArg.consequent.type === "CallExpression" || firstArg.alternate.type === "CallExpression"))) { + return; + } + context.report({ + node: firstArg, + messageId: "noDirectCall" + }); + }, + // Handle cases where the initializer is passed as a function reference + VariableDeclarator(node) { + if (!node.init || node.init.type !== "CallExpression" || node.init.callee.name !== "useState" || node.init.arguments.length === 0) { + return; + } + const firstArg = node.init.arguments[0]; + if (firstArg.type === "Identifier" || firstArg.type === "ArrowFunctionExpression" && firstArg.body.type !== "CallExpression") { + return; + } + if (firstArg.type === "CallExpression" || firstArg.type === "MemberExpression" && firstArg.object.type === "CallExpression" || firstArg.type === "ConditionalExpression" && (firstArg.consequent.type === "CallExpression" || firstArg.alternate.type === "CallExpression")) { + context.report({ + node: firstArg, + messageId: "noDirectCall" + }); + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-useless-compose.js +var no_useless_compose_exports = {}; +__export(no_useless_compose_exports, { + create: () => create19 +}); +import lodashGet9 from "lodash/get.js"; +var message12 = CONST_default.MESSAGE.NO_USELESS_COMPOSE; +function create19(context) { + return { + CallExpression(node) { + const name6 = lodashGet9(node, "callee.name"); + if (!name6) { + return; + } + if (name6 !== "compose") { + return; + } + if (node.arguments.length !== 1) { + return; + } + context.report({ + node, + message: message12 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-actions-set-data.js +var prefer_actions_set_data_exports = {}; +__export(prefer_actions_set_data_exports, { + create: () => create20 +}); +import _10 from "underscore"; +import lodashGet10 from "lodash/get.js"; +var message13 = CONST_default.MESSAGE.PREFER_ACTIONS_SET_DATA; +function isDataSettingMethod(methodName) { + return _10.includes(["set", "merge", "mergeCollection"], methodName); +} +function create20(context) { + return { + MemberExpression(node) { + const filename = context.getFilename(); + if (!isOnyxMethodCall(node)) { + return; + } + if (isInTestFile(context.getFilename())) { + return; + } + const methodName = lodashGet10(node, "property.name"); + if (!isDataSettingMethod(methodName) || isInActionFile(context.getFilename(filename))) { + return; + } + context.report({ + node, + message: message13 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-early-return.js +var prefer_early_return_exports = {}; +__export(prefer_early_return_exports, { + create: () => create21 +}); +var defaultMaximumStatements = 0; +var message14 = CONST_default.MESSAGE.PREFER_EARLY_RETURN; +function create21(context) { + const options = context.options[0] || { + maximumStatements: defaultMaximumStatements + }; + const maxStatements = options.maximumStatements; + function isLonelyIfStatement(statement) { + return statement.type === "IfStatement" && statement.alternate == null; + } + function isOffendingConsequent(consequent) { + return consequent.type === "ExpressionStatement" && maxStatements === 0 || consequent.type === "BlockStatement" && consequent.body.length > maxStatements; + } + function isOffendingIfStatement(statement) { + return isLonelyIfStatement(statement) && isOffendingConsequent(statement.consequent); + } + function hasSimplifiableConditionalBody(functionBody) { + const body = functionBody.body; + return functionBody.type === "BlockStatement" && body.length === 1 && isOffendingIfStatement(body[0]); + } + function checkFunctionBody(functionNode) { + const body = functionNode.body; + if (hasSimplifiableConditionalBody(body)) { + context.report( + body, + message14 + ); + } + } + return { + FunctionDeclaration: checkFunctionBody, + FunctionExpression: checkFunctionBody, + ArrowFunctionExpression: checkFunctionBody + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-localization.js +var prefer_localization_exports = {}; +__export(prefer_localization_exports, { + create: () => create22 +}); +import _11 from "underscore"; +var message15 = CONST_default.MESSAGE.PREFER_LOCALIZATION; +function isDateUtilsUsedAsProperty(node) { + const dateUtilsProperties = _11.filter(node.properties, (property) => { + const keyNode = property.key; + const valueNode = property.value; + if (!isCallFromObject(keyNode, "DateUtils")) { + return false; + } + if (getMethodName(keyNode) !== "getMicroseconds") { + return false; + } + if (isCallFromObject(valueNode, "Localize")) { + return false; + } + return true; + }); + return dateUtilsProperties.length > 0; +} +function create22(context) { + return { + ObjectExpression: (node) => { + if (!isInActionFile(context.getFilename())) { + return; + } + if (!isDateUtilsUsedAsProperty(node)) { + return; + } + context.report(node, message15); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-narrow-hook-dependencies.js +var prefer_narrow_hook_dependencies_exports = {}; +__export(prefer_narrow_hook_dependencies_exports, { + create: () => create23, + meta: () => meta9, + name: () => name5 +}); +var name5 = "prefer-narrow-hook-dependencies"; +var meta9 = { + type: "suggestion", + docs: { + description: "Enforce narrowing down React hook dependency arrays to specific properties instead of entire objects", + recommended: "error" + }, + fixable: "code", + schema: [ + { + type: "object", + properties: { + stableObjectPatterns: { + type: "array", + items: { + type: "string" + }, + description: 'Array of regex pattern strings for stable objects that should not be narrowed (e.g., "^styles?$", "^theme$")' + } + }, + additionalProperties: false + } + ], + messages: { + narrowDependency: 'Dependency "{{dependency}}" is too broad. Use specific properties instead: {{properties}}' + } +}; +var HOOKS_WITH_DEPS = /* @__PURE__ */ new Set([ + "useEffect", + "useLayoutEffect", + "useCallback", + "useMemo" +]); +function isStableObjectPattern(depName, patterns) { + return patterns.some((pattern) => pattern.test(depName)); +} +function hasCurrentPropertyAccess(depName, memberExpressions) { + const currentAccessPattern = `${depName}.current`; + for (const expr of memberExpressions) { + if (expr.startsWith(currentAccessPattern)) { + return true; + } + } + return false; +} +function isReactHook(node) { + return node.type === "CallExpression" && node.callee.type === "Identifier" && HOOKS_WITH_DEPS.has(node.callee.name); +} +function getDependencyArray(hookCall) { + const depArray = hookCall.arguments[1]; + if (!depArray || depArray.type !== "ArrayExpression") { + return null; + } + return depArray; +} +function getObjectName(node) { + let current = node; + while (current) { + if (current.type === "MemberExpression") { + current = current.object; + } else if (current.type === "Identifier") { + return current.name; + } else { + return null; + } + } + return null; +} +function getMemberExpressionPath(node) { + const parts = []; + let current = node; + while (current) { + if (current.type === "MemberExpression") { + if (current.computed) { + return null; + } + if (current.property.type === "Identifier") { + parts.unshift({ + name: current.property.name, + optional: current.optional || false + }); + } else { + return null; + } + current = current.object; + } else if (current.type === "Identifier") { + parts.unshift({ + name: current.name, + optional: false + }); + break; + } else { + return null; + } + } + if (parts.length === 0) { + return null; + } + let result = parts[0].name; + for (let i = 1; i < parts.length; i++) { + const separator = parts[i].optional ? "?." : "."; + result += separator + parts[i].name; + } + return result; +} +function traverseAST(node, visitor, visited = /* @__PURE__ */ new Set()) { + if (!node || !node.type || visited.has(node)) { + return; + } + visited.add(node); + visitor(node); + for (const key of Object.keys(node)) { + if (key === "parent" || key === "type" || key === "range" || key === "loc") { + continue; + } + const value = node[key]; + if (Array.isArray(value)) { + for (const item of value) { + if (item && typeof item === "object" && item.type) { + traverseAST(item, visitor, visited); + } + } + } else if (value && typeof value === "object" && value.type) { + traverseAST(value, visitor, visited); + } + } +} +function collectMemberExpressions(node, memberExpressions, directUsages, visited = /* @__PURE__ */ new Set()) { + traverseAST(node, (currentNode) => { + if (currentNode.type === "MemberExpression") { + if (currentNode.parent && currentNode.parent.type === "CallExpression" && currentNode.parent.callee === currentNode) { + const objectName = getObjectName(currentNode); + if (objectName) { + directUsages.add(objectName); + } + return; + } + if (currentNode.computed) { + const objectName = getObjectName(currentNode.object); + if (objectName) { + directUsages.add(objectName); + } + return; + } + const fullPath = getMemberExpressionPath(currentNode); + if (fullPath) { + memberExpressions.add(fullPath); + } + } + if (currentNode.type === "Identifier" && currentNode.parent) { + const parent = currentNode.parent; + if (parent.type === "MemberExpression" && parent.property === currentNode && !parent.computed) { + return; + } + if (parent.type === "MemberExpression" && parent.object === currentNode) { + return; + } + directUsages.add(currentNode.name); + } + if (currentNode.type === "SpreadElement") { + if (currentNode.argument && currentNode.argument.type === "Identifier") { + directUsages.add(currentNode.argument.name); + } + } + if (currentNode.type === "JSXSpreadAttribute") { + if (currentNode.argument && currentNode.argument.type === "Identifier") { + directUsages.add(currentNode.argument.name); + } + } + if (currentNode.type === "ForOfStatement" || currentNode.type === "ForInStatement") { + if (currentNode.right && currentNode.right.type === "Identifier") { + directUsages.add(currentNode.right.name); + } + } + }, visited); +} +function getLeafProperties(properties) { + if (properties.length === 0) { + return properties; + } + const sortedByLength = [...properties].sort((a, b) => a.length - b.length); + const prefixes = /* @__PURE__ */ new Set(); + for (let i = 0; i < sortedByLength.length; i++) { + const current = sortedByLength[i]; + for (let j = i + 1; j < sortedByLength.length; j++) { + const longer = sortedByLength[j]; + if (longer.startsWith(`${current}.`) || longer.startsWith(`${current}?.`)) { + prefixes.add(current); + break; + } + } + } + return properties.filter((prop) => !prefixes.has(prop)); +} +function create23(context) { + const options = context.options[0] || {}; + const patternStrings = options.stableObjectPatterns || []; + const stableObjectPatterns = patternStrings.map((pattern) => new RegExp(pattern)); + return { + CallExpression(node) { + if (!isReactHook(node)) { + return; + } + const depsArray = getDependencyArray(node); + if (!depsArray || depsArray.elements.length === 0) { + return; + } + const callback = node.arguments[0]; + if (!callback || callback.type !== "FunctionExpression" && callback.type !== "ArrowFunctionExpression") { + return; + } + const memberExpressions = /* @__PURE__ */ new Set(); + const directUsages = /* @__PURE__ */ new Set(); + const visited = /* @__PURE__ */ new Set(); + collectMemberExpressions( + callback.body, + memberExpressions, + directUsages, + visited + ); + for (const depElement of depsArray.elements) { + if (!depElement) { + continue; + } + if (depElement.type !== "Identifier") { + continue; + } + const depName = depElement.name; + if (directUsages.has(depName)) { + continue; + } + if (isStableObjectPattern(depName, stableObjectPatterns)) { + continue; + } + if (hasCurrentPropertyAccess(depName, memberExpressions)) { + continue; + } + const depWithDot = `${depName}.`; + const depWithOptional = `${depName}?.`; + const accessedProperties = []; + for (const expr of memberExpressions) { + if (expr === depName) { + continue; + } + if (expr.startsWith(depWithDot) || expr.startsWith(depWithOptional)) { + accessedProperties.push(expr); + } + } + const leafProperties = getLeafProperties(accessedProperties); + if (leafProperties.length > 0) { + const propertiesList = leafProperties.join(", "); + context.report({ + node: depElement, + messageId: "narrowDependency", + data: { + dependency: depName, + properties: propertiesList + }, + fix(fixer) { + return fixer.replaceText(depElement, propertiesList); + } + }); + } + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-onyx-connect-in-libs.js +var prefer_onyx_connect_in_libs_exports = {}; +__export(prefer_onyx_connect_in_libs_exports, { + create: () => create24 +}); +import lodashGet11 from "lodash/get.js"; +var message16 = CONST_default.MESSAGE.PREFER_ONYX_CONNECT_IN_LIBS; +function isInLibs(filename) { + return filename.includes("/src/libs/"); +} +function create24(context) { + return { + MemberExpression(node) { + const filename = context.getFilename(); + if (!isOnyxMethodCall(node)) { + return; + } + if (isInTestFile(context.getFilename())) { + return; + } + const methodName = lodashGet11(node, "property.name"); + if (methodName !== "connect" || isInLibs(filename)) { + return; + } + context.report({ + node, + message: message16 + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth.js +var prefer_shouldUseNarrowLayout_instead_of_isSmallScreenWidth_exports = {}; +__export(prefer_shouldUseNarrowLayout_instead_of_isSmallScreenWidth_exports, { + create: () => create25, + meta: () => meta10 +}); +import { AST_NODE_TYPES } from "./syntax-rule-utilities.mjs"; +import _12 from "underscore"; +var meta10 = { + type: "problem", + docs: { + description: "Warn against using isSmallScreenWidth from useResponsiveLayout and suggest using shouldUseNarrowLayout instead." + }, + schema: [] +}; +function create25(context) { + const sourceCode = context.sourceCode ?? context.getSourceCode(); + return { + VariableDeclarator(node) { + if (!node.init || !node.init.callee || node.init.callee.name !== "useResponsiveLayout") { + return; + } + if (node.id.type === AST_NODE_TYPES.ObjectPattern) { + for (const property of node.id.properties) { + if (!property.key || property.key.name !== "isSmallScreenWidth") { + continue; + } + context.report({ + node: property, + message: CONST_default.MESSAGE.PREFER_SHOULD_USE_NARROW_LAYOUT_INSTEAD_OF_IS_SMALL_SCREEN_WIDTH + }); + } + } + const scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope(); + const variableName = node.id.name; + const variableUsages = _12.filter( + scope.references, + (reference) => reference.identifier.name === variableName + ); + for (const usage of variableUsages) { + const parent = usage.identifier.parent; + if (parent.type === AST_NODE_TYPES.MemberExpression && parent.property.name === "isSmallScreenWidth") { + context.report({ + node: parent.property, + message: CONST_default.MESSAGE.PREFER_SHOULD_USE_NARROW_LAYOUT_INSTEAD_OF_IS_SMALL_SCREEN_WIDTH + }); + } + if (parent.type === AST_NODE_TYPES.VariableDeclarator && parent.id.type === AST_NODE_TYPES.ObjectPattern) { + for (const property of parent.id.properties) { + if (!property.key || property.key.name !== "isSmallScreenWidth") { + continue; + } + context.report({ + node: property, + message: CONST_default.MESSAGE.PREFER_SHOULD_USE_NARROW_LAYOUT_INSTEAD_OF_IS_SMALL_SCREEN_WIDTH + }); + } + } + } + }, + MemberExpression(node) { + if (node.object.type !== "CallExpression" || node.object.callee.name !== "useResponsiveLayout" || node.property.name !== "isSmallScreenWidth") { + return; + } + context.report({ + node, + message: CONST_default.MESSAGE.PREFER_SHOULD_USE_NARROW_LAYOUT_INSTEAD_OF_IS_SMALL_SCREEN_WIDTH + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-type-fest.js +var prefer_type_fest_exports = {}; +__export(prefer_type_fest_exports, { + create: () => create26, + meta: () => meta11 +}); +import { AST_NODE_TYPES as AST_NODE_TYPES2 } from "./syntax-rule-utilities.mjs"; + +// eslint-config-expensify/eslint-plugin-expensify/utils/imports.js +import _13 from "underscore"; +function addNamedImport(context, fixer, importNode, importName, importPath, importAsType = false) { + const fixes = []; + if (importNode) { + const alreadyImported = _13.some(importNode.specifiers, (specifier) => specifier.imported.name === importName); + if (!alreadyImported) { + const lastSpecifier = importNode.specifiers[importNode.specifiers.length - 1]; + fixes.push(fixer.insertTextAfter(lastSpecifier, `, ${importName}`)); + } + } else { + fixes.push( + fixer.insertTextBefore( + context.getSourceCode().ast.body[0], + `import ${importAsType ? "type " : ""}{${importName}} from '${importPath}'; +` + ) + ); + } + return fixes; +} + +// eslint-config-expensify/eslint-plugin-expensify/prefer-type-fest.js +var { PREFER_TYPE_FEST_VALUE_OF, PREFER_TYPE_FEST_TUPLE_TO_UNION } = CONST_default.MESSAGE; +var meta11 = { + type: "suggestion", + docs: { + description: "Enforce using type-fest utility types for better type readability" + }, + fixable: "code" +}; +function create26(context) { + let typeFestImport; + return { + Program(node) { + for (const statement of node.body) { + if (statement.type !== "ImportDeclaration" || statement.source.value !== "type-fest") { + continue; + } + typeFestImport = statement; + } + }, + TSIndexedAccessType(node) { + const objectType = node.objectType; + const indexType = node.indexType; + if (!objectType || !indexType) { + return; + } + if (objectType?.type !== AST_NODE_TYPES2.TSTypeQuery) { + return; + } + if (objectType?.exprName?.type === AST_NODE_TYPES2.Identifier) { + const objectTypeText = context.getSourceCode().getText(objectType.exprName); + if (indexType?.type === AST_NODE_TYPES2.TSTypeOperator && indexType?.operator === "keyof") { + const indexTypeText = context.getSourceCode().getText(indexType.typeAnnotation.typeName); + if (objectTypeText && objectTypeText === indexTypeText) { + context.report({ + node, + message: PREFER_TYPE_FEST_VALUE_OF, + fix: (fixer) => { + const fixes = [fixer.replaceText(node, `ValueOf`)]; + fixes.push(...addNamedImport(context, fixer, typeFestImport, "ValueOf", "type-fest", true)); + return fixes; + } + }); + } + } + if (indexType?.type === AST_NODE_TYPES2.TSNumberKeyword) { + context.report({ + node, + message: PREFER_TYPE_FEST_TUPLE_TO_UNION, + fix: (fixer) => { + const fixes = [fixer.replaceText(node, `TupleToUnion`)]; + fixes.push(...addNamedImport(context, fixer, typeFestImport, "TupleToUnion", "type-fest", true)); + return fixes; + } + }); + } + } + if (objectType?.exprName?.type === AST_NODE_TYPES2.TSQualifiedName) { + const objectTypeText = context.getSourceCode().getText(objectType.exprName); + if (indexType?.type === AST_NODE_TYPES2.TSTypeOperator && indexType?.operator === "keyof") { + const indexTypeText = context.getSourceCode().getText(indexType.typeAnnotation.exprName); + if (objectTypeText && objectTypeText === indexTypeText) { + context.report({ + node, + message: PREFER_TYPE_FEST_VALUE_OF, + fix: (fixer) => { + const fixes = [fixer.replaceText(node, `ValueOf`)]; + fixes.push(...addNamedImport(context, fixer, typeFestImport, "ValueOf", "type-fest", true)); + return fixes; + } + }); + } + } + if (indexType?.type === AST_NODE_TYPES2.TSNumberKeyword) { + context.report({ + node, + message: PREFER_TYPE_FEST_TUPLE_TO_UNION, + fix: (fixer) => { + const fixes = [fixer.replaceText(node, `TupleToUnion`)]; + fixes.push(...addNamedImport(context, fixer, typeFestImport, "TupleToUnion", "type-fest", true)); + return fixes; + } + }); + } + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/use-double-negation-instead-of-boolean.js +var use_double_negation_instead_of_boolean_exports = {}; +__export(use_double_negation_instead_of_boolean_exports, { + create: () => create27, + meta: () => meta12 +}); +var message17 = CONST_default.MESSAGE.USE_DOUBLE_NEGATION_INSTEAD_OF_BOOLEAN; +var meta12 = { + fixable: "code" +}; +function create27(context) { + return { + CallExpression(node) { + if (node.callee.type !== "Identifier" || node.callee.name !== "Boolean" || node.arguments.length !== 1) { + return; + } + const argument = node.arguments[0]; + const sourceCode = context.getSourceCode(); + const argumentText = sourceCode.getText(argument); + let fixedText = `!!${argumentText}`; + if (argument.type === "LogicalExpression" || argument.type === "BinaryExpression") { + fixedText = `!!(${argumentText})`; + } + context.report({ + node, + message: message17, + fix(fixer) { + return fixer.replaceText(node, fixedText); + } + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-inline-useOnyx-selector.js +var no_inline_useOnyx_selector_exports = {}; +__export(no_inline_useOnyx_selector_exports, { + create: () => create28, + meta: () => meta13 +}); +import _14 from "underscore"; +var meta13 = { + type: "problem", + docs: { + description: "Enforces that useOnyx hook calls do not use inline selectors.", + recommended: "error" + }, + schema: [], + messages: { + noInlineSelector: "useOnyx() selector should not be defined inline.\n\nShared selectors: define in a dedicated selectors/ directory for reuse.\n\nLocal selectors: define in the same file outside the component.\n\nContextual selectors: define as useCallback inside the component with relevant dependencies.", + noNonMemoizedSelector: "useOnyx() selector defined within component should be memoized with useCallback.\n\nWrap the selector function with useCallback to prevent unnecessary re-renders:\n\nconst memoizedSelector = useCallback((val) => ({...}), [dependencies]);", + noInlineFactorySelector: "useOnyx() selector is produced by a factory call inline, so it returns a new function on every render.\n\nMemoize the factory result with useMemo so the reference stays stable:\n\nconst memoizedSelector = useMemo(() => makeSelector(arg), [arg]);" + } +}; +function create28(context) { + const componentStack = []; + function isReactComponent(node) { + if (node.type === "FunctionDeclaration" && node.id) { + return /^[A-Z]/.test(node.id.name); + } + if (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") { + const parent = node.parent; + if (parent && parent.type === "VariableDeclarator" && parent.id && parent.id.name) { + return /^[A-Z]/.test(parent.id.name); + } + } + return false; + } + function isDefinedWithMemoHook(variable) { + if (!variable || variable.defs.length === 0) { + return false; + } + const def = variable.defs[0]; + if (def.node.init && def.node.init.type === "CallExpression") { + const callee = def.node.init.callee; + return callee.name === "useCallback" || callee.name === "useMemo"; + } + return false; + } + function isDefinedInCurrentComponent(variable) { + if (!variable || variable.defs.length === 0 || componentStack.length === 0) { + return false; + } + const currentComponent = componentStack[componentStack.length - 1]; + const variableDef = variable.defs[0]; + return variableDef.node.range[0] >= currentComponent.body.range[0] && variableDef.node.range[1] <= currentComponent.body.range[1]; + } + function isInlineSelector(property) { + return property.type === "Property" && property.key.name === "selector" && (property.value.type === "FunctionExpression" || property.value.type === "ArrowFunctionExpression"); + } + function isFactorySelector(property) { + return property.type === "Property" && property.key.name === "selector" && property.value.type === "CallExpression"; + } + function hasInlineSelector(objectExpression) { + return _14.some(objectExpression.properties, isInlineSelector); + } + function hasFactorySelector(objectExpression) { + return _14.some(objectExpression.properties, isFactorySelector); + } + function checkSelectorMemoization(selectorName, node) { + if (componentStack.length === 0) { + return; + } + const variable = resolveVariable(context, selectorName, node); + if (!variable) { + return; + } + if (isDefinedInCurrentComponent(variable) && !isDefinedWithMemoHook(variable)) { + const init = variable.defs[0].node.init; + if (init && init.type === "CallExpression") { + context.report({ + node, + messageId: "noInlineFactorySelector" + }); + return; + } + context.report({ + node, + messageId: "noNonMemoizedSelector" + }); + } + } + return { + FunctionDeclaration(node) { + if (!isReactComponent(node)) { + return; + } + componentStack.push(node); + }, + "FunctionDeclaration:exit": function(node) { + if (!isReactComponent(node)) { + return; + } + componentStack.pop(); + }, + FunctionExpression(node) { + if (!isReactComponent(node)) { + return; + } + componentStack.push(node); + }, + "FunctionExpression:exit": function(node) { + if (!isReactComponent(node)) { + return; + } + componentStack.pop(); + }, + ArrowFunctionExpression(node) { + if (!isReactComponent(node)) { + return; + } + componentStack.push(node); + }, + "ArrowFunctionExpression:exit": function(node) { + if (!isReactComponent(node)) { + return; + } + componentStack.pop(); + }, + VariableDeclarator(node) { + if (!node.init || node.init.type !== "CallExpression" || node.init.callee.name !== "useOnyx") { + return; + } + if (node.init.arguments.length < 2) { + return; + } + const optionsArgument = node.init.arguments[1]; + switch (optionsArgument.type) { + case "ObjectExpression": { + if (hasInlineSelector(optionsArgument)) { + context.report({ + node: node.init, + messageId: "noInlineSelector" + }); + } else if (hasFactorySelector(optionsArgument)) { + context.report({ + node: node.init, + messageId: "noInlineFactorySelector" + }); + } else { + const selectorProperty = findProperty(optionsArgument, "selector"); + if (selectorProperty && selectorProperty.value.type === "Identifier") { + checkSelectorMemoization(selectorProperty.value.name, node.init); + } + } + break; + } + case "Identifier": { + const resolvedValue = getVariableAsObject(context, optionsArgument.name, node); + if (resolvedValue && hasInlineSelector(resolvedValue)) { + context.report({ + node: node.init, + messageId: "noInlineSelector" + }); + } else if (resolvedValue && hasFactorySelector(resolvedValue)) { + context.report({ + node: node.init, + messageId: "noInlineFactorySelector" + }); + } else if (resolvedValue) { + const selectorProperty = findProperty(resolvedValue, "selector"); + if (selectorProperty && selectorProperty.value.type === "Identifier") { + checkSelectorMemoization(selectorProperty.value.name, node.init); + } + } + break; + } + default: { + break; + } + } + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/no-object-keys-includes.js +var no_object_keys_includes_exports = {}; +__export(no_object_keys_includes_exports, { + create: () => create29, + meta: () => meta14 +}); +var message18 = CONST_default.MESSAGE.NO_OBJECT_KEYS_INCLUDES; +var meta14 = { + type: "problem", + docs: { + description: "Disallow using Object.keys().includes() for performance reasons", + category: "Performance", + recommended: true + }, + fixable: "code", + messages: { + noObjectKeysIncludes: message18 + }, + schema: [] +}; +function create29(context) { + const sourceCode = context.getSourceCode(); + function isObjectKeysCall(node) { + return node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.object.type === "Identifier" && node.callee.object.name === "Object" && node.callee.property.type === "Identifier" && node.callee.property.name === "keys" && node.arguments.length === 1; + } + return { + CallExpression(node) { + if (node.callee.type !== "MemberExpression" || node.callee.property.type !== "Identifier" || node.callee.property.name !== "includes" || !isObjectKeysCall(node.callee.object)) { + return; + } + const objectArg = node.callee.object.arguments[0]; + const includesArg = node.arguments[0]; + const objectText = sourceCode.getText(objectArg); + const includesText = sourceCode.getText(includesArg); + context.report({ + node, + messageId: "noObjectKeysIncludes", + data: { + object: objectText, + key: includesText + }, + fix(fixer) { + const parent = node.parent; + const needsParens = parent && (parent.type === "LogicalExpression" || parent.type === "ConditionalExpression" || parent.type === "UnaryExpression" && parent.operator === "!"); + const replacement = needsParens ? `(${includesText} in ${objectText})` : `${includesText} in ${objectText}`; + return fixer.replaceText(node, replacement); + } + }); + } + }; +} + +// eslint-config-expensify/eslint-plugin-expensify/use-periods-for-error-messages.js +var use_periods_for_error_messages_exports = {}; +__export(use_periods_for_error_messages_exports, { + create: () => create30, + meta: () => meta15 +}); +import _15 from "underscore"; +var message19 = CONST_default.MESSAGE.USE_PERIODS_ERROR_MESSAGES; +var meta15 = { + type: "suggestion", + docs: { + description: "Enforce using periods at the end of error messages" + }, + fixable: "code" +}; +function create30(context) { + const variableDeclarators = /* @__PURE__ */ new Map(); + return { + VariableDeclarator(node) { + variableDeclarators.set(node.id.name, node); + }, + NewExpression(node) { + if (!node.callee || node.callee.name !== "Error" || node.arguments.length === 0) { + return; + } + const errArg = node.arguments[0]; + let errorMessage = ""; + if (errArg.type === "Literal") { + errorMessage = errArg.value; + } else if (variableDeclarators.has(errArg.name)) { + const variableDeclarator = variableDeclarators.get(errArg.name); + if (variableDeclarator.init.type !== "Literal") { + return; + } + errorMessage = variableDeclarator.init.value; + } else { + return; + } + const sentenceCount = _15.filter(errorMessage.split("."), (sentence) => sentence.trim().length > 0).length; + const doesViolateRule = sentenceCount > 1 && !errorMessage.endsWith(".") || sentenceCount === 1 && errorMessage.endsWith("."); + if (doesViolateRule) { + context.report({ + node, + message: message19, + fix(fixer) { + if (errorMessage.type !== "Literal") { + return; + } + const fixedMessage = `${errorMessage}.`; + return fixer.replaceText(node.arguments[0], `'${fixedMessage}'`); + } + }); + } + } + }; +} + +// expensify-rules-entry.mjs +var rules = { "context-provider-split-values": context_provider_split_values_exports, "no-acc-spread-in-reduce": no_acc_spread_in_reduce_exports, "no-api-in-views": no_api_in_views_exports, "no-api-side-effects-method": no_api_side_effects_method_exports, "no-beta-handler": no_beta_handler_exports, "no-call-actions-from-actions": no_call_actions_from_actions_exports, "no-deep-equal-in-memo": no_deep_equal_in_memo_exports, "no-default-id-values": no_default_id_values_exports, "no-default-props": no_default_props_exports, "no-inline-named-export": no_inline_named_export_exports, "no-multiple-api-calls": no_multiple_api_calls_exports, "no-multiple-onyx-in-file": no_multiple_onyx_in_file_exports, "no-negated-variables": no_negated_variables_exports, "no-onyx-connect": no_onyx_connect_exports, "no-set-or-map-return-in-useOnyx-selector": no_set_or_map_return_in_useOnyx_selector_exports, "no-thenable-actions-in-views": no_thenable_actions_in_views_exports, "no-unstable-hook-defaults": no_unstable_hook_defaults_exports, "no-use-state-initializer-functions": no_use_state_initializer_functions_exports, "no-useless-compose": no_useless_compose_exports, "prefer-actions-set-data": prefer_actions_set_data_exports, "prefer-early-return": prefer_early_return_exports, "prefer-localization": prefer_localization_exports, "prefer-narrow-hook-dependencies": prefer_narrow_hook_dependencies_exports, "prefer-onyx-connect-in-libs": prefer_onyx_connect_in_libs_exports, "prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth": prefer_shouldUseNarrowLayout_instead_of_isSmallScreenWidth_exports, "prefer-type-fest": prefer_type_fest_exports, "use-double-negation-instead-of-boolean": use_double_negation_instead_of_boolean_exports, "no-inline-useOnyx-selector": no_inline_useOnyx_selector_exports, "no-object-keys-includes": no_object_keys_includes_exports, "use-periods-for-error-messages": use_periods_for_error_messages_exports }; +export { + CONST_default as CONST, + isInTestFile, + rules +}; diff --git a/config/oxlint/vendor/naming-convention.mjs b/config/oxlint/vendor/naming-convention.mjs new file mode 100644 index 000000000000..aabc6d1e8b5e --- /dev/null +++ b/config/oxlint/vendor/naming-convention.mjs @@ -0,0 +1,1435 @@ +/** Generated by build.mjs. See README.md for source versions, licenses and supported naming selectors. */ +import {createRequire as createVendorRequire} from 'node:module'; +const require = createVendorRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __esm = (fn, res, err) => function __init() { + if (err) throw err[0]; + try { + return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res; + } catch (e) { + throw err = [e], e; + } +}; +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// syntax-only:naming-utilities +var naming_utilities_exports = {}; +__export(naming_utilities_exports, { + createRule: () => createRule, + getEnumNames: () => getEnumNames, + getParserServices: () => getParserServices, + requiresQuoting: () => requiresQuoting +}); +import { ESLintUtils } from "./syntax-rule-utilities.mjs"; +import { isIdentifierName } from "@babel/helper-validator-identifier"; +function getParserServices(_context, allowWithoutFullTypeInformation) { + if (!allowWithoutFullTypeInformation) { + throw new Error("Type-based naming selectors require a type-aware rule."); + } + return {}; +} +var RuleCreator, createRule, getEnumNames, requiresQuoting; +var init_naming_utilities = __esm({ + "syntax-only:naming-utilities"() { + ({ RuleCreator } = ESLintUtils); + createRule = RuleCreator((name) => "https://typescript-eslint.io/rules/" + name); + getEnumNames = (values) => Object.keys(values).filter((key) => !/^\d+$/.test(key)); + requiresQuoting = (name) => !isIdentifierName(name); + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/enums.js +var require_enums = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/enums.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.TypeModifiers = exports.Modifiers = exports.MetaSelectors = exports.Selectors = exports.UnderscoreOptions = exports.PredefinedFormats = void 0; + var PredefinedFormats; + (function(PredefinedFormats2) { + PredefinedFormats2[PredefinedFormats2["camelCase"] = 1] = "camelCase"; + PredefinedFormats2[PredefinedFormats2["strictCamelCase"] = 2] = "strictCamelCase"; + PredefinedFormats2[PredefinedFormats2["PascalCase"] = 3] = "PascalCase"; + PredefinedFormats2[PredefinedFormats2["StrictPascalCase"] = 4] = "StrictPascalCase"; + PredefinedFormats2[PredefinedFormats2["snake_case"] = 5] = "snake_case"; + PredefinedFormats2[PredefinedFormats2["UPPER_CASE"] = 6] = "UPPER_CASE"; + })(PredefinedFormats || (exports.PredefinedFormats = PredefinedFormats = {})); + var UnderscoreOptions; + (function(UnderscoreOptions2) { + UnderscoreOptions2[UnderscoreOptions2["forbid"] = 1] = "forbid"; + UnderscoreOptions2[UnderscoreOptions2["allow"] = 2] = "allow"; + UnderscoreOptions2[UnderscoreOptions2["require"] = 3] = "require"; + UnderscoreOptions2[UnderscoreOptions2["requireDouble"] = 4] = "requireDouble"; + UnderscoreOptions2[UnderscoreOptions2["allowDouble"] = 5] = "allowDouble"; + UnderscoreOptions2[UnderscoreOptions2["allowSingleOrDouble"] = 6] = "allowSingleOrDouble"; + })(UnderscoreOptions || (exports.UnderscoreOptions = UnderscoreOptions = {})); + var Selectors; + (function(Selectors2) { + Selectors2[Selectors2["variable"] = 1] = "variable"; + Selectors2[Selectors2["function"] = 2] = "function"; + Selectors2[Selectors2["parameter"] = 4] = "parameter"; + Selectors2[Selectors2["parameterProperty"] = 8] = "parameterProperty"; + Selectors2[Selectors2["classicAccessor"] = 16] = "classicAccessor"; + Selectors2[Selectors2["enumMember"] = 32] = "enumMember"; + Selectors2[Selectors2["classMethod"] = 64] = "classMethod"; + Selectors2[Selectors2["objectLiteralMethod"] = 128] = "objectLiteralMethod"; + Selectors2[Selectors2["typeMethod"] = 256] = "typeMethod"; + Selectors2[Selectors2["classProperty"] = 512] = "classProperty"; + Selectors2[Selectors2["objectLiteralProperty"] = 1024] = "objectLiteralProperty"; + Selectors2[Selectors2["typeProperty"] = 2048] = "typeProperty"; + Selectors2[Selectors2["autoAccessor"] = 4096] = "autoAccessor"; + Selectors2[Selectors2["class"] = 8192] = "class"; + Selectors2[Selectors2["interface"] = 16384] = "interface"; + Selectors2[Selectors2["typeAlias"] = 32768] = "typeAlias"; + Selectors2[Selectors2["enum"] = 65536] = "enum"; + Selectors2[Selectors2["typeParameter"] = 131072] = "typeParameter"; + Selectors2[Selectors2["import"] = 262144] = "import"; + })(Selectors || (exports.Selectors = Selectors = {})); + var MetaSelectors; + (function(MetaSelectors2) { + MetaSelectors2[MetaSelectors2["default"] = -1] = "default"; + MetaSelectors2[MetaSelectors2["variableLike"] = 7] = "variableLike"; + MetaSelectors2[MetaSelectors2["memberLike"] = 8184] = "memberLike"; + MetaSelectors2[MetaSelectors2["typeLike"] = 253952] = "typeLike"; + MetaSelectors2[MetaSelectors2["method"] = 448] = "method"; + MetaSelectors2[MetaSelectors2["property"] = 3584] = "property"; + MetaSelectors2[MetaSelectors2["accessor"] = 4112] = "accessor"; + })(MetaSelectors || (exports.MetaSelectors = MetaSelectors = {})); + var Modifiers; + (function(Modifiers2) { + Modifiers2[Modifiers2["const"] = 1] = "const"; + Modifiers2[Modifiers2["readonly"] = 2] = "readonly"; + Modifiers2[Modifiers2["static"] = 4] = "static"; + Modifiers2[Modifiers2["public"] = 8] = "public"; + Modifiers2[Modifiers2["protected"] = 16] = "protected"; + Modifiers2[Modifiers2["private"] = 32] = "private"; + Modifiers2[Modifiers2["#private"] = 64] = "#private"; + Modifiers2[Modifiers2["abstract"] = 128] = "abstract"; + Modifiers2[Modifiers2["destructured"] = 256] = "destructured"; + Modifiers2[Modifiers2["global"] = 512] = "global"; + Modifiers2[Modifiers2["exported"] = 1024] = "exported"; + Modifiers2[Modifiers2["unused"] = 2048] = "unused"; + Modifiers2[Modifiers2["requiresQuotes"] = 4096] = "requiresQuotes"; + Modifiers2[Modifiers2["override"] = 8192] = "override"; + Modifiers2[Modifiers2["async"] = 16384] = "async"; + Modifiers2[Modifiers2["default"] = 32768] = "default"; + Modifiers2[Modifiers2["namespace"] = 65536] = "namespace"; + })(Modifiers || (exports.Modifiers = Modifiers = {})); + var TypeModifiers; + (function(TypeModifiers2) { + TypeModifiers2[TypeModifiers2["boolean"] = 131072] = "boolean"; + TypeModifiers2[TypeModifiers2["string"] = 262144] = "string"; + TypeModifiers2[TypeModifiers2["number"] = 524288] = "number"; + TypeModifiers2[TypeModifiers2["function"] = 1048576] = "function"; + TypeModifiers2[TypeModifiers2["array"] = 2097152] = "array"; + })(TypeModifiers || (exports.TypeModifiers = TypeModifiers = {})); + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/shared.js +var require_shared = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/shared.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.selectorTypeToMessageString = selectorTypeToMessageString; + exports.isMetaSelector = isMetaSelector; + exports.isMethodOrPropertySelector = isMethodOrPropertySelector; + var enums_1 = require_enums(); + function selectorTypeToMessageString(selectorType) { + const notCamelCase = selectorType.replaceAll(/([A-Z])/g, " $1"); + return notCamelCase.charAt(0).toUpperCase() + notCamelCase.slice(1); + } + function isMetaSelector(selector) { + return selector in enums_1.MetaSelectors; + } + function isMethodOrPropertySelector(selector) { + return selector === enums_1.MetaSelectors.method || selector === enums_1.MetaSelectors.property; + } + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/format.js +var require_format = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/format.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.PredefinedFormatToCheckFunction = void 0; + var enums_1 = require_enums(); + function isPascalCase(name) { + return name.length === 0 || name[0] === name[0].toUpperCase() && !name.includes("_"); + } + function isStrictPascalCase(name) { + return name.length === 0 || name[0] === name[0].toUpperCase() && hasStrictCamelHumps(name, true); + } + function isCamelCase(name) { + return name.length === 0 || name[0] === name[0].toLowerCase() && !name.includes("_"); + } + function isStrictCamelCase(name) { + return name.length === 0 || name[0] === name[0].toLowerCase() && hasStrictCamelHumps(name, false); + } + function hasStrictCamelHumps(name, isUpper) { + function isUppercaseChar(char) { + return char === char.toUpperCase() && char !== char.toLowerCase(); + } + if (name.startsWith("_")) { + return false; + } + for (let i = 1; i < name.length; ++i) { + if (name[i] === "_") { + return false; + } + if (isUpper === isUppercaseChar(name[i])) { + if (isUpper) { + return false; + } + } else { + isUpper = !isUpper; + } + } + return true; + } + function isSnakeCase(name) { + return name.length === 0 || name === name.toLowerCase() && validateUnderscores(name); + } + function isUpperCase(name) { + return name.length === 0 || name === name.toUpperCase() && validateUnderscores(name); + } + function validateUnderscores(name) { + if (name.startsWith("_")) { + return false; + } + let wasUnderscore = false; + for (let i = 1; i < name.length; ++i) { + if (name[i] === "_") { + if (wasUnderscore) { + return false; + } + wasUnderscore = true; + } else { + wasUnderscore = false; + } + } + return !wasUnderscore; + } + exports.PredefinedFormatToCheckFunction = { + [enums_1.PredefinedFormats.camelCase]: isCamelCase, + [enums_1.PredefinedFormats.PascalCase]: isPascalCase, + [enums_1.PredefinedFormats.snake_case]: isSnakeCase, + [enums_1.PredefinedFormats.strictCamelCase]: isStrictCamelCase, + [enums_1.PredefinedFormats.StrictPascalCase]: isStrictPascalCase, + [enums_1.PredefinedFormats.UPPER_CASE]: isUpperCase + }; + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/validator.js +var require_validator = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/validator.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.createValidator = createValidator; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var util_1 = (init_naming_utilities(), __toCommonJS(naming_utilities_exports)); + var enums_1 = require_enums(); + var format_1 = require_format(); + var shared_1 = require_shared(); + function createValidator(type, context, allConfigs) { + const selectorType = enums_1.Selectors[type]; + const configs = allConfigs.filter((c) => (c.selector & selectorType) !== 0 || c.selector === enums_1.MetaSelectors.default).sort((a, b) => { + if (a.selector === b.selector) { + return b.modifierWeight - a.modifierWeight; + } + const aIsMeta = (0, shared_1.isMetaSelector)(a.selector); + const bIsMeta = (0, shared_1.isMetaSelector)(b.selector); + if (aIsMeta && !bIsMeta) { + return 1; + } + if (!aIsMeta && bIsMeta) { + return -1; + } + const aIsMethodOrProperty = (0, shared_1.isMethodOrPropertySelector)(a.selector); + const bIsMethodOrProperty = (0, shared_1.isMethodOrPropertySelector)(b.selector); + if (aIsMethodOrProperty && !bIsMethodOrProperty) { + return -1; + } + if (!aIsMethodOrProperty && bIsMethodOrProperty) { + return 1; + } + return b.selector - a.selector; + }); + return (node, modifiers = /* @__PURE__ */ new Set()) => { + const originalName = node.type === utils_1.AST_NODE_TYPES.Identifier || node.type === utils_1.AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`; + for (const config of configs) { + if (config.filter?.regex.test(originalName) !== config.filter?.match) { + continue; + } + if (config.modifiers?.some((modifier) => !modifiers.has(modifier))) { + continue; + } + if (!isCorrectType(node, config, context, selectorType)) { + continue; + } + let name = originalName; + name = validateUnderscore("leading", config, name, node, originalName); + if (name == null) { + return; + } + name = validateUnderscore("trailing", config, name, node, originalName); + if (name == null) { + return; + } + name = validateAffix("prefix", config, name, node, originalName); + if (name == null) { + return; + } + name = validateAffix("suffix", config, name, node, originalName); + if (name == null) { + return; + } + if (!validateCustom(config, name, node, originalName)) { + return; + } + if (!validatePredefinedFormat(config, name, node, originalName, modifiers)) { + return; + } + return; + } + }; + function formatReportData({ affixes, count, custom, formats, originalName, position, processedName }) { + return { + affixes: affixes?.join(", "), + count, + formats: formats?.map((f) => enums_1.PredefinedFormats[f]).join(", "), + name: originalName, + position, + processedName, + regex: custom?.regex.toString(), + regexMatch: custom?.match === true ? "match" : custom?.match === false ? "not match" : null, + type: (0, shared_1.selectorTypeToMessageString)(type) + }; + } + function validateUnderscore(position, config, name, node, originalName) { + const option = position === "leading" ? config.leadingUnderscore : config.trailingUnderscore; + if (!option) { + return name; + } + const hasSingleUnderscore = position === "leading" ? () => name.startsWith("_") : () => name.endsWith("_"); + const trimSingleUnderscore = position === "leading" ? () => name.slice(1) : () => name.slice(0, -1); + const hasDoubleUnderscore = position === "leading" ? () => name.startsWith("__") : () => name.endsWith("__"); + const trimDoubleUnderscore = position === "leading" ? () => name.slice(2) : () => name.slice(0, -2); + switch (option) { + // ALLOW - no conditions as the user doesn't care if it's there or not + case enums_1.UnderscoreOptions.allow: { + if (hasSingleUnderscore()) { + return trimSingleUnderscore(); + } + return name; + } + case enums_1.UnderscoreOptions.allowDouble: { + if (hasDoubleUnderscore()) { + return trimDoubleUnderscore(); + } + return name; + } + case enums_1.UnderscoreOptions.allowSingleOrDouble: { + if (hasDoubleUnderscore()) { + return trimDoubleUnderscore(); + } + if (hasSingleUnderscore()) { + return trimSingleUnderscore(); + } + return name; + } + // FORBID + case enums_1.UnderscoreOptions.forbid: { + if (hasSingleUnderscore()) { + context.report({ + data: formatReportData({ + count: "one", + originalName, + position + }), + messageId: "unexpectedUnderscore", + node + }); + return null; + } + return name; + } + // REQUIRE + case enums_1.UnderscoreOptions.require: { + if (!hasSingleUnderscore()) { + context.report({ + data: formatReportData({ + count: "one", + originalName, + position + }), + messageId: "missingUnderscore", + node + }); + return null; + } + return trimSingleUnderscore(); + } + case enums_1.UnderscoreOptions.requireDouble: { + if (!hasDoubleUnderscore()) { + context.report({ + data: formatReportData({ + count: "two", + originalName, + position + }), + messageId: "missingUnderscore", + node + }); + return null; + } + return trimDoubleUnderscore(); + } + } + } + function validateAffix(position, config, name, node, originalName) { + const affixes = config[position]; + if (!affixes || affixes.length === 0) { + return name; + } + for (const affix of affixes) { + const hasAffix = position === "prefix" ? name.startsWith(affix) : name.endsWith(affix); + const trimAffix = position === "prefix" ? () => name.slice(affix.length) : () => name.slice(0, -affix.length); + if (hasAffix) { + return trimAffix(); + } + } + context.report({ + data: formatReportData({ + affixes, + originalName, + position + }), + messageId: "missingAffix", + node + }); + return null; + } + function validateCustom(config, name, node, originalName) { + const custom = config.custom; + if (!custom) { + return true; + } + const result = custom.regex.test(name); + if (custom.match && result) { + return true; + } + if (!custom.match && !result) { + return true; + } + context.report({ + data: formatReportData({ + custom, + originalName + }), + messageId: "satisfyCustom", + node + }); + return false; + } + function validatePredefinedFormat(config, name, node, originalName, modifiers) { + const formats = config.format; + if (!formats?.length) { + return true; + } + if (!modifiers.has(enums_1.Modifiers.requiresQuotes)) { + for (const format of formats) { + const checker = format_1.PredefinedFormatToCheckFunction[format]; + if (checker(name)) { + return true; + } + } + } + context.report({ + data: formatReportData({ + formats, + originalName, + processedName: name + }), + messageId: originalName === name ? "doesNotMatchFormat" : "doesNotMatchFormatTrimmed", + node + }); + return false; + } + } + var SelectorsAllowedToHaveTypes = enums_1.Selectors.variable | enums_1.Selectors.parameter | enums_1.Selectors.classProperty | enums_1.Selectors.objectLiteralProperty | enums_1.Selectors.typeProperty | enums_1.Selectors.parameterProperty | enums_1.Selectors.classicAccessor; + function isCorrectType(node, config, context, selector) { + if (config.types == null) { + return true; + } + if ((SelectorsAllowedToHaveTypes & selector) === 0) { + return true; + } + const services = (0, util_1.getParserServices)(context); + const checker = services.program.getTypeChecker(); + const type = services.getTypeAtLocation(node).getNonNullableType(); + for (const allowedType of config.types) { + switch (allowedType) { + case enums_1.TypeModifiers.array: + if (isAllTypesMatch(type, (t) => checker.isArrayType(t) || checker.isTupleType(t))) { + return true; + } + break; + case enums_1.TypeModifiers.function: + if (isAllTypesMatch(type, (t) => t.getCallSignatures().length > 0)) { + return true; + } + break; + case enums_1.TypeModifiers.boolean: + case enums_1.TypeModifiers.number: + case enums_1.TypeModifiers.string: { + const typeString = checker.typeToString( + // this will resolve things like true => boolean, 'a' => string and 1 => number + checker.getWidenedType(checker.getBaseTypeOfLiteralType(type)) + ); + const allowedTypeString = enums_1.TypeModifiers[allowedType]; + if (typeString === allowedTypeString) { + return true; + } + break; + } + } + } + return false; + } + function isAllTypesMatch(type, cb) { + if (type.isUnion()) { + return type.types.every((t) => cb(t)); + } + return cb(type); + } + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/parse-options.js +var require_parse_options = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/parse-options.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.parseOptions = parseOptions; + var util_1 = (init_naming_utilities(), __toCommonJS(naming_utilities_exports)); + var enums_1 = require_enums(); + var shared_1 = require_shared(); + var validator_1 = require_validator(); + function normalizeOption(option) { + let weight = 0; + option.modifiers?.forEach((mod) => { + weight |= enums_1.Modifiers[mod]; + }); + option.types?.forEach((mod) => { + weight |= enums_1.TypeModifiers[mod]; + }); + if (option.filter) { + weight |= 1 << 30; + } + const normalizedOption = { + // format options + custom: option.custom ? { + match: option.custom.match, + regex: new RegExp(option.custom.regex, "u") + } : null, + filter: option.filter != null ? typeof option.filter === "string" ? { + match: true, + regex: new RegExp(option.filter, "u") + } : { + match: option.filter.match, + regex: new RegExp(option.filter.regex, "u") + } : null, + format: option.format ? option.format.map((f) => enums_1.PredefinedFormats[f]) : null, + leadingUnderscore: option.leadingUnderscore != null ? enums_1.UnderscoreOptions[option.leadingUnderscore] : null, + modifiers: option.modifiers?.map((m) => enums_1.Modifiers[m]) ?? null, + prefix: option.prefix && option.prefix.length > 0 ? option.prefix : null, + suffix: option.suffix && option.suffix.length > 0 ? option.suffix : null, + trailingUnderscore: option.trailingUnderscore != null ? enums_1.UnderscoreOptions[option.trailingUnderscore] : null, + types: option.types?.map((m) => enums_1.TypeModifiers[m]) ?? null, + // calculated ordering weight based on modifiers + modifierWeight: weight + }; + const selectors = Array.isArray(option.selector) ? option.selector : [option.selector]; + return selectors.map((selector) => ({ + selector: (0, shared_1.isMetaSelector)(selector) ? enums_1.MetaSelectors[selector] : enums_1.Selectors[selector], + ...normalizedOption + })); + } + function parseOptions(context) { + const normalizedOptions = context.options.flatMap(normalizeOption); + return Object.fromEntries((0, util_1.getEnumNames)(enums_1.Selectors).map((k) => [ + k, + (0, validator_1.createValidator)(k, context, normalizedOptions) + ])); + } + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/schema.js +var require_schema = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/schema.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.SCHEMA = void 0; + var util_1 = (init_naming_utilities(), __toCommonJS(naming_utilities_exports)); + var enums_1 = require_enums(); + var $DEFS = { + // enums + predefinedFormats: { + enum: (0, util_1.getEnumNames)(enums_1.PredefinedFormats), + type: "string" + }, + typeModifiers: { + enum: (0, util_1.getEnumNames)(enums_1.TypeModifiers), + type: "string" + }, + underscoreOptions: { + enum: (0, util_1.getEnumNames)(enums_1.UnderscoreOptions), + type: "string" + }, + // repeated types + formatOptionsConfig: { + oneOf: [ + { + additionalItems: false, + items: { + $ref: "#/$defs/predefinedFormats" + }, + type: "array" + }, + { + type: "null" + } + ] + }, + matchRegexConfig: { + additionalProperties: false, + properties: { + match: { type: "boolean" }, + regex: { type: "string" } + }, + required: ["match", "regex"], + type: "object" + }, + prefixSuffixConfig: { + additionalItems: false, + items: { + minLength: 1, + type: "string" + }, + type: "array" + } + }; + var UNDERSCORE_SCHEMA = { + $ref: "#/$defs/underscoreOptions" + }; + var PREFIX_SUFFIX_SCHEMA = { + $ref: "#/$defs/prefixSuffixConfig" + }; + var MATCH_REGEX_SCHEMA = { + $ref: "#/$defs/matchRegexConfig" + }; + var FORMAT_OPTIONS_PROPERTIES = { + custom: MATCH_REGEX_SCHEMA, + failureMessage: { + type: "string" + }, + format: { + $ref: "#/$defs/formatOptionsConfig" + }, + leadingUnderscore: UNDERSCORE_SCHEMA, + prefix: PREFIX_SUFFIX_SCHEMA, + suffix: PREFIX_SUFFIX_SCHEMA, + trailingUnderscore: UNDERSCORE_SCHEMA + }; + function selectorSchema(selectorString, allowType, modifiers) { + const selector = { + filter: { + oneOf: [ + { + minLength: 1, + type: "string" + }, + MATCH_REGEX_SCHEMA + ] + }, + selector: { + enum: [selectorString], + type: "string" + } + }; + if (modifiers && modifiers.length > 0) { + selector.modifiers = { + additionalItems: false, + items: { + enum: modifiers, + type: "string" + }, + type: "array" + }; + } + if (allowType) { + selector.types = { + additionalItems: false, + items: { + $ref: "#/$defs/typeModifiers" + }, + type: "array" + }; + } + return [ + { + additionalProperties: false, + description: `Selector '${selectorString}'`, + properties: { + ...FORMAT_OPTIONS_PROPERTIES, + ...selector + }, + required: ["selector", "format"], + type: "object" + } + ]; + } + function selectorsSchema() { + return { + additionalProperties: false, + description: "Multiple selectors in one config", + properties: { + ...FORMAT_OPTIONS_PROPERTIES, + filter: { + oneOf: [ + { + minLength: 1, + type: "string" + }, + MATCH_REGEX_SCHEMA + ] + }, + modifiers: { + additionalItems: false, + items: { + enum: (0, util_1.getEnumNames)(enums_1.Modifiers), + type: "string" + }, + type: "array" + }, + selector: { + additionalItems: false, + items: { + enum: [...(0, util_1.getEnumNames)(enums_1.MetaSelectors), ...(0, util_1.getEnumNames)(enums_1.Selectors)], + type: "string" + }, + type: "array" + }, + types: { + additionalItems: false, + items: { + $ref: "#/$defs/typeModifiers" + }, + type: "array" + } + }, + required: ["selector", "format"], + type: "object" + }; + } + exports.SCHEMA = { + $defs: $DEFS, + additionalItems: false, + items: { + oneOf: [ + selectorsSchema(), + ...selectorSchema("default", false, (0, util_1.getEnumNames)(enums_1.Modifiers)), + ...selectorSchema("variableLike", false, ["unused", "async"]), + ...selectorSchema("variable", true, [ + "const", + "destructured", + "exported", + "global", + "unused", + "async" + ]), + ...selectorSchema("function", false, [ + "exported", + "global", + "unused", + "async" + ]), + ...selectorSchema("parameter", true, ["destructured", "unused"]), + ...selectorSchema("memberLike", false, [ + "abstract", + "private", + "#private", + "protected", + "public", + "readonly", + "requiresQuotes", + "static", + "override", + "async" + ]), + ...selectorSchema("classProperty", true, [ + "abstract", + "private", + "#private", + "protected", + "public", + "readonly", + "requiresQuotes", + "static", + "override" + ]), + ...selectorSchema("objectLiteralProperty", true, [ + "public", + "requiresQuotes" + ]), + ...selectorSchema("typeProperty", true, [ + "public", + "readonly", + "requiresQuotes" + ]), + ...selectorSchema("parameterProperty", true, [ + "private", + "protected", + "public", + "readonly" + ]), + ...selectorSchema("property", true, [ + "abstract", + "private", + "#private", + "protected", + "public", + "readonly", + "requiresQuotes", + "static", + "override", + "async" + ]), + ...selectorSchema("classMethod", false, [ + "abstract", + "private", + "#private", + "protected", + "public", + "requiresQuotes", + "static", + "override", + "async" + ]), + ...selectorSchema("objectLiteralMethod", false, [ + "public", + "requiresQuotes", + "async" + ]), + ...selectorSchema("typeMethod", false, ["public", "requiresQuotes"]), + ...selectorSchema("method", false, [ + "abstract", + "private", + "#private", + "protected", + "public", + "requiresQuotes", + "static", + "override", + "async" + ]), + ...selectorSchema("classicAccessor", true, [ + "abstract", + "private", + "protected", + "public", + "requiresQuotes", + "static", + "override" + ]), + ...selectorSchema("autoAccessor", true, [ + "abstract", + "private", + "protected", + "public", + "requiresQuotes", + "static", + "override" + ]), + ...selectorSchema("accessor", true, [ + "abstract", + "private", + "protected", + "public", + "requiresQuotes", + "static", + "override" + ]), + ...selectorSchema("enumMember", false, ["requiresQuotes"]), + ...selectorSchema("typeLike", false, ["abstract", "exported", "unused"]), + ...selectorSchema("class", false, ["abstract", "exported", "unused"]), + ...selectorSchema("interface", false, ["exported", "unused"]), + ...selectorSchema("typeAlias", false, ["exported", "unused"]), + ...selectorSchema("enum", false, ["exported", "unused"]), + ...selectorSchema("typeParameter", false, ["unused"]), + ...selectorSchema("import", false, ["default", "namespace"]) + ] + }, + type: "array" + }; + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/index.js +var require_naming_convention_utils = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention-utils/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.selectorTypeToMessageString = exports.SCHEMA = exports.parseOptions = exports.Modifiers = void 0; + var enums_1 = require_enums(); + Object.defineProperty(exports, "Modifiers", { enumerable: true, get: function() { + return enums_1.Modifiers; + } }); + var parse_options_1 = require_parse_options(); + Object.defineProperty(exports, "parseOptions", { enumerable: true, get: function() { + return parse_options_1.parseOptions; + } }); + var schema_1 = require_schema(); + Object.defineProperty(exports, "SCHEMA", { enumerable: true, get: function() { + return schema_1.SCHEMA; + } }); + var shared_1 = require_shared(); + Object.defineProperty(exports, "selectorTypeToMessageString", { enumerable: true, get: function() { + return shared_1.selectorTypeToMessageString; + } }); + } +}); + +// @typescript-eslint/eslint-plugin/dist/rules/naming-convention.js +var require_naming_convention = __commonJS({ + "@typescript-eslint/eslint-plugin/dist/rules/naming-convention.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + var scope_manager_1 = __require("@typescript-eslint/scope-manager"); + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var util_1 = (init_naming_utilities(), __toCommonJS(naming_utilities_exports)); + var naming_convention_utils_1 = require_naming_convention_utils(); + var defaultCamelCaseAllTheThingsConfig = [ + { + format: ["camelCase"], + leadingUnderscore: "allow", + selector: "default", + trailingUnderscore: "allow" + }, + { + format: ["camelCase", "PascalCase"], + selector: "import" + }, + { + format: ["camelCase", "UPPER_CASE"], + leadingUnderscore: "allow", + selector: "variable", + trailingUnderscore: "allow" + }, + { + format: ["PascalCase"], + selector: "typeLike" + } + ]; + exports.default = (0, util_1.createRule)({ + name: "naming-convention", + meta: { + type: "suggestion", + docs: { + description: "Enforce naming conventions for everything across a codebase", + // technically only requires type checking if the user uses "type" modifiers + frozen: true, + requiresTypeChecking: true + }, + messages: { + doesNotMatchFormat: "{{type}} name `{{name}}` must match one of the following formats: {{formats}}", + doesNotMatchFormatTrimmed: "{{type}} name `{{name}}` trimmed as `{{processedName}}` must match one of the following formats: {{formats}}", + missingAffix: "{{type}} name `{{name}}` must have one of the following {{position}}es: {{affixes}}", + missingUnderscore: "{{type}} name `{{name}}` must have {{count}} {{position}} underscore(s).", + satisfyCustom: "{{type}} name `{{name}}` must {{regexMatch}} the RegExp: {{regex}}", + unexpectedUnderscore: "{{type}} name `{{name}}` must not have a {{position}} underscore." + }, + schema: naming_convention_utils_1.SCHEMA + }, + defaultOptions: defaultCamelCaseAllTheThingsConfig, + create(contextWithoutDefaults) { + const context = contextWithoutDefaults.options.length > 0 ? contextWithoutDefaults : ( + // only apply the defaults when the user provides no config + Object.setPrototypeOf({ + options: defaultCamelCaseAllTheThingsConfig + }, contextWithoutDefaults) + ); + const validators = (0, naming_convention_utils_1.parseOptions)(context); + const compilerOptions = (0, util_1.getParserServices)(context, true).program?.getCompilerOptions() ?? {}; + function handleMember(validator, node, modifiers) { + const key = node.key; + if (requiresQuoting2(key, compilerOptions.target)) { + modifiers.add(naming_convention_utils_1.Modifiers.requiresQuotes); + } + validator(key, modifiers); + } + function getMemberModifiers(node) { + const modifiers = /* @__PURE__ */ new Set(); + if ("key" in node && node.key.type === utils_1.AST_NODE_TYPES.PrivateIdentifier) { + modifiers.add(naming_convention_utils_1.Modifiers["#private"]); + } else if (node.accessibility) { + modifiers.add(naming_convention_utils_1.Modifiers[node.accessibility]); + } else { + modifiers.add(naming_convention_utils_1.Modifiers.public); + } + if (node.static) { + modifiers.add(naming_convention_utils_1.Modifiers.static); + } + if ("readonly" in node && node.readonly) { + modifiers.add(naming_convention_utils_1.Modifiers.readonly); + } + if ("override" in node && node.override) { + modifiers.add(naming_convention_utils_1.Modifiers.override); + } + if (node.type === utils_1.AST_NODE_TYPES.TSAbstractPropertyDefinition || node.type === utils_1.AST_NODE_TYPES.TSAbstractMethodDefinition || node.type === utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty) { + modifiers.add(naming_convention_utils_1.Modifiers.abstract); + } + return modifiers; + } + if (context.options.some((option) => option.types?.length || option.modifiers?.includes("unused"))) { + throw new Error("Type and unused naming selectors are not supported by this syntax-only rule."); + } + const unusedVariables = /* @__PURE__ */ new Set(); + function isUnused(name, initialScope) { + let variable = null; + let scope = initialScope; + while (scope) { + variable = scope.set.get(name) ?? null; + if (variable) { + break; + } + scope = scope.upper; + } + if (!variable) { + return false; + } + return unusedVariables.has(variable); + } + function isDestructured(id) { + return ( + // `const { x }` + // does not match `const { x: y }` + id.parent.type === utils_1.AST_NODE_TYPES.Property && id.parent.shorthand || // `const { x = 2 }` + // does not match const `{ x: y = 2 }` + id.parent.type === utils_1.AST_NODE_TYPES.AssignmentPattern && id.parent.parent.type === utils_1.AST_NODE_TYPES.Property && id.parent.parent.shorthand + ); + } + function isAsyncMemberOrProperty(propertyOrMemberNode) { + return Boolean("value" in propertyOrMemberNode && propertyOrMemberNode.value && "async" in propertyOrMemberNode.value && propertyOrMemberNode.value.async); + } + function isAsyncVariableIdentifier(id) { + return Boolean("async" in id.parent && id.parent.async || "init" in id.parent && id.parent.init && "async" in id.parent.init && id.parent.init.async); + } + const selectors = { + // #region import + "FunctionDeclaration, TSDeclareFunction, FunctionExpression": { + handler: (node, validator) => { + if (node.id == null) { + return; + } + const modifiers = /* @__PURE__ */ new Set(); + const scope = context.sourceCode.getScope(node).upper; + if (isGlobal(scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.global); + } + if (isExported(node, node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.exported); + } + if (isUnused(node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + if (node.async) { + modifiers.add(naming_convention_utils_1.Modifiers.async); + } + validator(node.id, modifiers); + }, + validator: validators.function + }, + // #endregion + // #region variable + "ImportDefaultSpecifier, ImportNamespaceSpecifier, ImportSpecifier": { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set(); + switch (node.type) { + case utils_1.AST_NODE_TYPES.ImportDefaultSpecifier: + modifiers.add(naming_convention_utils_1.Modifiers.default); + break; + case utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier: + modifiers.add(naming_convention_utils_1.Modifiers.namespace); + break; + case utils_1.AST_NODE_TYPES.ImportSpecifier: + if (node.imported.type === utils_1.AST_NODE_TYPES.Identifier && node.imported.name !== "default") { + return; + } + modifiers.add(naming_convention_utils_1.Modifiers.default); + break; + } + validator(node.local, modifiers); + }, + validator: validators.import + }, + // #endregion + // #region function + VariableDeclarator: { + handler: (node, validator) => { + const identifiers = getIdentifiersFromPattern(node.id); + const baseModifiers = /* @__PURE__ */ new Set(); + const parent = node.parent; + if (parent.kind === "const") { + baseModifiers.add(naming_convention_utils_1.Modifiers.const); + } + if (isGlobal(context.sourceCode.getScope(node))) { + baseModifiers.add(naming_convention_utils_1.Modifiers.global); + } + identifiers.forEach((id) => { + const modifiers = new Set(baseModifiers); + if (isDestructured(id)) { + modifiers.add(naming_convention_utils_1.Modifiers.destructured); + } + const scope = context.sourceCode.getScope(id); + if (isExported(parent, id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.exported); + } + if (isUnused(id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + if (isAsyncVariableIdentifier(id)) { + modifiers.add(naming_convention_utils_1.Modifiers.async); + } + validator(id, modifiers); + }); + }, + validator: validators.variable + }, + // #endregion function + // #region parameter + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': { + handler: (node, validator) => { + const modifiers = getMemberModifiers(node); + handleMember(validator, node, modifiers); + }, + validator: validators.classProperty + }, + // #endregion parameter + // #region parameterProperty + ':not(ObjectPattern) > Property[computed = false][kind = "init"][value.type != "ArrowFunctionExpression"][value.type != "FunctionExpression"][value.type != "TSEmptyBodyFunctionExpression"]': { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set([naming_convention_utils_1.Modifiers.public]); + handleMember(validator, node, modifiers); + }, + validator: validators.objectLiteralProperty + }, + // #endregion parameterProperty + // #region property + [[ + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "ArrowFunctionExpression"]', + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "FunctionExpression"]', + ':matches(PropertyDefinition, TSAbstractPropertyDefinition)[computed = false][value.type = "TSEmptyBodyFunctionExpression"]', + ':matches(MethodDefinition, TSAbstractMethodDefinition)[computed = false][kind = "method"]' + ].join(", ")]: { + handler: (node, validator) => { + const modifiers = getMemberModifiers(node); + if (isAsyncMemberOrProperty(node)) { + modifiers.add(naming_convention_utils_1.Modifiers.async); + } + handleMember(validator, node, modifiers); + }, + validator: validators.classMethod + }, + [[ + 'MethodDefinition[computed = false]:matches([kind = "get"], [kind = "set"])', + 'TSAbstractMethodDefinition[computed = false]:matches([kind="get"], [kind="set"])' + ].join(", ")]: { + handler: (node, validator) => { + const modifiers = getMemberModifiers(node); + handleMember(validator, node, modifiers); + }, + validator: validators.classicAccessor + }, + [[ + 'Property[computed = false][kind = "init"][value.type = "ArrowFunctionExpression"]', + 'Property[computed = false][kind = "init"][value.type = "FunctionExpression"]', + 'Property[computed = false][kind = "init"][value.type = "TSEmptyBodyFunctionExpression"]' + ].join(", ")]: { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set([naming_convention_utils_1.Modifiers.public]); + if (isAsyncMemberOrProperty(node)) { + modifiers.add(naming_convention_utils_1.Modifiers.async); + } + handleMember(validator, node, modifiers); + }, + validator: validators.objectLiteralMethod + }, + // #endregion property + // #region method + [[ + "TSMethodSignature[computed = false]", + 'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type = "TSFunctionType"]' + ].join(", ")]: { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set([naming_convention_utils_1.Modifiers.public]); + handleMember(validator, node, modifiers); + }, + validator: validators.typeMethod + }, + [[ + utils_1.AST_NODE_TYPES.AccessorProperty, + utils_1.AST_NODE_TYPES.TSAbstractAccessorProperty + ].join(", ")]: { + handler: (node, validator) => { + const modifiers = getMemberModifiers(node); + handleMember(validator, node, modifiers); + }, + validator: validators.autoAccessor + }, + "FunctionDeclaration, TSDeclareFunction, TSEmptyBodyFunctionExpression, FunctionExpression, ArrowFunctionExpression": { + handler: (node, validator) => { + node.params.forEach((param) => { + if (param.type === utils_1.AST_NODE_TYPES.TSParameterProperty) { + return; + } + const identifiers = getIdentifiersFromPattern(param); + identifiers.forEach((i) => { + const modifiers = /* @__PURE__ */ new Set(); + if (isDestructured(i)) { + modifiers.add(naming_convention_utils_1.Modifiers.destructured); + } + if (isUnused(i.name, context.sourceCode.getScope(i))) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + validator(i, modifiers); + }); + }); + }, + validator: validators.parameter + }, + // #endregion method + // #region accessor + 'Property[computed = false]:matches([kind = "get"], [kind = "set"])': { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set([naming_convention_utils_1.Modifiers.public]); + handleMember(validator, node, modifiers); + }, + validator: validators.classicAccessor + }, + TSParameterProperty: { + handler: (node, validator) => { + const modifiers = getMemberModifiers(node); + const identifiers = getIdentifiersFromPattern(node.parameter); + identifiers.forEach((i) => { + validator(i, modifiers); + }); + }, + validator: validators.parameterProperty + }, + // #endregion accessor + // #region autoAccessor + 'TSPropertySignature[computed = false][typeAnnotation.typeAnnotation.type != "TSFunctionType"]': { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set([naming_convention_utils_1.Modifiers.public]); + if (node.readonly) { + modifiers.add(naming_convention_utils_1.Modifiers.readonly); + } + handleMember(validator, node, modifiers); + }, + validator: validators.typeProperty + }, + // #endregion autoAccessor + // #region enumMember + // computed is optional, so can't do [computed = false] + "ClassDeclaration, ClassExpression": { + handler: (node, validator) => { + const id = node.id; + if (id == null) { + return; + } + const modifiers = /* @__PURE__ */ new Set(); + const scope = context.sourceCode.getScope(node).upper; + if (node.abstract) { + modifiers.add(naming_convention_utils_1.Modifiers.abstract); + } + if (isExported(node, id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.exported); + } + if (isUnused(id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + validator(id, modifiers); + }, + validator: validators.class + }, + // #endregion enumMember + // #region class + TSEnumDeclaration: { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set(); + const scope = context.sourceCode.getScope(node).upper; + if (isExported(node, node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.exported); + } + if (isUnused(node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + validator(node.id, modifiers); + }, + validator: validators.enum + }, + // #endregion class + // #region interface + TSEnumMember: { + handler: (node, validator) => { + const id = node.id; + const modifiers = /* @__PURE__ */ new Set(); + if (requiresQuoting2(id, compilerOptions.target)) { + modifiers.add(naming_convention_utils_1.Modifiers.requiresQuotes); + } + validator(id, modifiers); + }, + validator: validators.enumMember + }, + // #endregion interface + // #region typeAlias + TSInterfaceDeclaration: { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set(); + const scope = context.sourceCode.getScope(node); + if (isExported(node, node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.exported); + } + if (isUnused(node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + validator(node.id, modifiers); + }, + validator: validators.interface + }, + // #endregion typeAlias + // #region enum + TSTypeAliasDeclaration: { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set(); + const scope = context.sourceCode.getScope(node); + if (isExported(node, node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.exported); + } + if (isUnused(node.id.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + validator(node.id, modifiers); + }, + validator: validators.typeAlias + }, + // #endregion enum + // #region typeParameter + "TSTypeParameterDeclaration > TSTypeParameter": { + handler: (node, validator) => { + const modifiers = /* @__PURE__ */ new Set(); + const scope = context.sourceCode.getScope(node); + if (isUnused(node.name.name, scope)) { + modifiers.add(naming_convention_utils_1.Modifiers.unused); + } + validator(node.name, modifiers); + }, + validator: validators.typeParameter + } + // #endregion typeParameter + }; + return Object.fromEntries(Object.entries(selectors).map(([selector, { handler, validator }]) => { + return [ + selector, + (node) => { + handler(node, validator); + } + ]; + })); + } + }); + function getIdentifiersFromPattern(pattern) { + const identifiers = []; + const visitor = new scope_manager_1.PatternVisitor({}, pattern, (id) => identifiers.push(id)); + visitor.visit(pattern); + return identifiers; + } + function isExported(node, name, scope) { + if (node?.parent?.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration || node?.parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration) { + return true; + } + if (scope == null) { + return false; + } + const variable = scope.set.get(name); + if (variable) { + for (const ref of variable.references) { + const refParent = ref.identifier.parent; + if (refParent.type === utils_1.AST_NODE_TYPES.ExportDefaultDeclaration || refParent.type === utils_1.AST_NODE_TYPES.ExportSpecifier) { + return true; + } + } + } + return false; + } + function isGlobal(scope) { + if (scope == null) { + return false; + } + return scope.type === utils_1.TSESLint.Scope.ScopeType.global || scope.type === utils_1.TSESLint.Scope.ScopeType.module; + } + function requiresQuoting2(node, target) { + const name = node.type === utils_1.AST_NODE_TYPES.Identifier || node.type === utils_1.AST_NODE_TYPES.PrivateIdentifier ? node.name : `${node.value}`; + return (0, util_1.requiresQuoting)(name, target); + } + } +}); + +// naming-convention-entry.mjs +var import_naming_convention = __toESM(require_naming_convention(), 1); +var naming_convention_entry_default = import_naming_convention.default.default; +export { + naming_convention_entry_default as default +}; diff --git a/config/oxlint/vendor/storybook.mjs b/config/oxlint/vendor/storybook.mjs new file mode 100644 index 000000000000..93c05d778381 --- /dev/null +++ b/config/oxlint/vendor/storybook.mjs @@ -0,0 +1,1502 @@ +/** Generated by build.mjs. See README.md for source versions, licenses and supported naming selectors. */ +import {createRequire as createVendorRequire} from 'node:module'; +const require = createVendorRequire(import.meta.url); + +// eslint-plugin-storybook/dist/index.js +import CJS_COMPAT_NODE_URL_gwt72rbw0y6 from "node:url"; +import CJS_COMPAT_NODE_PATH_gwt72rbw0y6 from "node:path"; +import CJS_COMPAT_NODE_MODULE_gwt72rbw0y6 from "node:module"; +import { AST_NODE_TYPES } from "@typescript-eslint/types"; +import * as ASTUtils from "./syntax-rule-utilities.mjs"; +import { RuleCreator } from "./syntax-rule-utilities.mjs"; +import path from "path"; +import { AST_NODE_TYPES as AST_NODE_TYPES2 } from "@typescript-eslint/types"; +import { readFileSync } from "fs"; +import { relative, resolve, sep } from "path"; +var __filename = CJS_COMPAT_NODE_URL_gwt72rbw0y6.fileURLToPath(import.meta.url); +var __dirname = CJS_COMPAT_NODE_PATH_gwt72rbw0y6.dirname(__filename); +var require2 = CJS_COMPAT_NODE_MODULE_gwt72rbw0y6.createRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from == "object" || typeof from == "function") + for (let key of __getOwnPropNames(from)) + !__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var require_dist = __commonJS({ + "../../../node_modules/ts-dedent/dist/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.dedent = void 0; + function dedent2(templ) { + for (var values = [], _i = 1; _i < arguments.length; _i++) + values[_i - 1] = arguments[_i]; + var strings = Array.from(typeof templ == "string" ? [templ] : templ); + strings[strings.length - 1] = strings[strings.length - 1].replace(/\r?\n([\t ]*)$/, ""); + var indentLengths = strings.reduce(function(arr, str) { + var matches2 = str.match(/\n([\t ]+|(?!\s).)/g); + return matches2 ? arr.concat(matches2.map(function(match) { + var _a, _b; + return (_b = (_a = match.match(/[\t ]/g)) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0; + })) : arr; + }, []); + if (indentLengths.length) { + var pattern_1 = new RegExp(` +[ ]{` + Math.min.apply(Math, indentLengths) + "}", "g"); + strings = strings.map(function(str) { + return str.replace(pattern_1, ` +`); + }); + } + strings[0] = strings[0].replace(/^\r?\n/, ""); + var string = strings[0]; + return values.forEach(function(value, i) { + var endentations = string.match(/(?:^|\n)( *)$/), endentation = endentations ? endentations[1] : "", indentedValue = value; + typeof value == "string" && value.includes(` +`) && (indentedValue = String(value).split(` +`).map(function(str, i2) { + return i2 === 0 ? str : "" + endentation + str; + }).join(` +`)), string += indentedValue + strings[i + 1]; + }), string; + } + exports.dedent = dedent2; + exports.default = dedent2; + } +}); +var name = "eslint-plugin-storybook"; +var version = "10.6.2"; +var csf_default = { + plugins: ["storybook"], + overrides: [ + { + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/csf-component": "warn", + "storybook/default-exports": "error", + "storybook/hierarchy-separator": "warn", + "storybook/no-redundant-story-name": "warn", + "storybook/story-exports": "error" + } + }, + { + files: [".storybook/main.@(js|cjs|mjs|ts)"], + rules: { + "storybook/no-uninstalled-addons": "error" + } + } + ] +}; +var csf_strict_default = { + // This file is bundled in an index.js file at the root + // so the reference is relative to the src directory + extends: "./configs/csf", + overrides: [ + { + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/no-stories-of": "error", + "storybook/no-title-property-in-meta": "error" + } + } + ] +}; +var addon_interactions_default = { + plugins: ["storybook"], + overrides: [ + { + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/await-interactions": "error", + "storybook/context-in-play-function": "error", + "storybook/use-storybook-expect": "error", + "storybook/use-storybook-testing-library": "error" + } + }, + { + files: [".storybook/main.@(js|cjs|mjs|ts)"], + rules: { + "storybook/no-uninstalled-addons": "error" + } + } + ] +}; +var recommended_default = { + plugins: ["storybook"], + overrides: [ + { + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/await-interactions": "error", + "storybook/context-in-play-function": "error", + "storybook/default-exports": "error", + "storybook/hierarchy-separator": "warn", + "storybook/no-redundant-story-name": "warn", + "storybook/no-renderer-packages": "error", + "storybook/prefer-pascal-case": "warn", + "storybook/story-exports": "error", + "storybook/use-storybook-expect": "error", + "storybook/use-storybook-testing-library": "error" + } + }, + { + files: [".storybook/main.@(js|cjs|mjs|ts)"], + rules: { + "storybook/no-uninstalled-addons": "error" + } + } + ] +}; +var csf_default2 = [ + { + name: "storybook:csf:setup", + plugins: { + get storybook() { + return index_default; + } + } + }, + { + name: "storybook:csf:stories-rules", + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/csf-component": "warn", + "storybook/default-exports": "error", + "storybook/hierarchy-separator": "warn", + "storybook/no-redundant-story-name": "warn", + "storybook/story-exports": "error" + } + }, + { + name: "storybook:csf:main-rules", + files: [".storybook/main.@(js|cjs|mjs|ts)"], + rules: { + "storybook/no-uninstalled-addons": "error" + } + } +]; +var csf_strict_default2 = [ + ...csf_default2, + { + name: "storybook:csf-strict:rules", + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/no-stories-of": "error", + "storybook/no-title-property-in-meta": "error" + } + } +]; +var addon_interactions_default2 = [ + { + name: "storybook:addon-interactions:setup", + plugins: { + get storybook() { + return index_default; + } + } + }, + { + name: "storybook:addon-interactions:stories-rules", + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/await-interactions": "error", + "storybook/context-in-play-function": "error", + "storybook/use-storybook-expect": "error", + "storybook/use-storybook-testing-library": "error" + } + }, + { + name: "storybook:addon-interactions:main-rules", + files: [".storybook/main.@(js|cjs|mjs|ts)"], + rules: { + "storybook/no-uninstalled-addons": "error" + } + } +]; +var recommended_default2 = [ + { + name: "storybook:recommended:setup", + plugins: { + get storybook() { + return index_default; + } + } + }, + { + name: "storybook:recommended:stories-rules", + files: ["**/*.stories.@(ts|tsx|js|jsx|mjs|cjs)", "**/*.story.@(ts|tsx|js|jsx|mjs|cjs)"], + rules: { + "react-hooks/rules-of-hooks": "off", + "import-x/no-anonymous-default-export": "off", + "storybook/await-interactions": "error", + "storybook/context-in-play-function": "error", + "storybook/default-exports": "error", + "storybook/hierarchy-separator": "warn", + "storybook/no-redundant-story-name": "warn", + "storybook/no-renderer-packages": "error", + "storybook/prefer-pascal-case": "warn", + "storybook/story-exports": "error", + "storybook/use-storybook-expect": "error", + "storybook/use-storybook-testing-library": "error" + } + }, + { + name: "storybook:recommended:main-rules", + files: [".storybook/main.@(js|cjs|mjs|ts)"], + rules: { + "storybook/no-uninstalled-addons": "error" + } + } +]; +var isNodeOfType = (nodeType) => (node) => node?.type === nodeType; +var isAwaitExpression = isNodeOfType(AST_NODE_TYPES.AwaitExpression); +var isIdentifier = isNodeOfType(AST_NODE_TYPES.Identifier); +var isVariableDeclarator = isNodeOfType(AST_NODE_TYPES.VariableDeclarator); +var isArrayExpression = isNodeOfType(AST_NODE_TYPES.ArrayExpression); +var isArrowFunctionExpression = isNodeOfType(AST_NODE_TYPES.ArrowFunctionExpression); +var isBlockStatement = isNodeOfType(AST_NODE_TYPES.BlockStatement); +var isCallExpression = isNodeOfType(AST_NODE_TYPES.CallExpression); +var isExpressionStatement = isNodeOfType(AST_NODE_TYPES.ExpressionStatement); +var isVariableDeclaration = isNodeOfType(AST_NODE_TYPES.VariableDeclaration); +var isAssignmentExpression = isNodeOfType(AST_NODE_TYPES.AssignmentExpression); +var isSequenceExpression = isNodeOfType(AST_NODE_TYPES.SequenceExpression); +var isImportDeclaration = isNodeOfType(AST_NODE_TYPES.ImportDeclaration); +var isImportDefaultSpecifier = isNodeOfType(AST_NODE_TYPES.ImportDefaultSpecifier); +var isImportNamespaceSpecifier = isNodeOfType(AST_NODE_TYPES.ImportNamespaceSpecifier); +var isImportSpecifier = isNodeOfType(AST_NODE_TYPES.ImportSpecifier); +var isJSXAttribute = isNodeOfType(AST_NODE_TYPES.JSXAttribute); +var isLiteral = isNodeOfType(AST_NODE_TYPES.Literal); +var isMemberExpression = isNodeOfType(AST_NODE_TYPES.MemberExpression); +var isNewExpression = isNodeOfType(AST_NODE_TYPES.NewExpression); +var isObjectExpression = isNodeOfType(AST_NODE_TYPES.ObjectExpression); +var isObjectPattern = isNodeOfType(AST_NODE_TYPES.ObjectPattern); +var isProperty = isNodeOfType(AST_NODE_TYPES.Property); +var isSpreadElement = isNodeOfType(AST_NODE_TYPES.SpreadElement); +var isRestElement = isNodeOfType(AST_NODE_TYPES.RestElement); +var isReturnStatement = isNodeOfType(AST_NODE_TYPES.ReturnStatement); +var isFunctionDeclaration = isNodeOfType(AST_NODE_TYPES.FunctionDeclaration); +var isFunctionExpression = isNodeOfType(AST_NODE_TYPES.FunctionExpression); +var isProgram = isNodeOfType(AST_NODE_TYPES.Program); +var isTSTypeAliasDeclaration = isNodeOfType(AST_NODE_TYPES.TSTypeAliasDeclaration); +var isTSInterfaceDeclaration = isNodeOfType(AST_NODE_TYPES.TSInterfaceDeclaration); +var isTSAsExpression = isNodeOfType(AST_NODE_TYPES.TSAsExpression); +var isTSSatisfiesExpression = isNodeOfType(AST_NODE_TYPES.TSSatisfiesExpression); +var isTSNonNullExpression = isNodeOfType(AST_NODE_TYPES.TSNonNullExpression); +var isMetaProperty = isNodeOfType(AST_NODE_TYPES.MetaProperty); +function toStartCaseStr(str) { + return str.replace(/_/g, " ").replace(/-/g, " ").replace(/\./g, " ").replace(/([^\n])([A-Z])([a-z])/g, (str2, $1, $2, $3) => `${$1} ${$2}${$3}`).replace(/([a-z])([A-Z])/g, (str2, $1, $2) => `${$1} ${$2}`).replace(/([a-z])([0-9])/gi, (str2, $1, $2) => `${$1} ${$2}`).replace(/([0-9])([a-z])/gi, (str2, $1, $2) => `${$1} ${$2}`).replace(/(\s|^)(\w)/g, (str2, $1, $2) => `${$1}${$2.toUpperCase()}`).replace(/ +/g, " ").trim(); +} +function matches(storyKey, arrayOrRegex) { + return Array.isArray(arrayOrRegex) ? arrayOrRegex.includes(storyKey) : storyKey.match(arrayOrRegex); +} +var storyNameFromExport = (key) => toStartCaseStr(key); +function isExportStory(key, { includeStories, excludeStories }) { + return ( + // Babel's CommonJS interop adds __esModule; it is not a CSF story export. + key !== "__esModule" && (!includeStories || matches(key, includeStories)) && (!excludeStories || !matches(key, excludeStories)) + ); +} +var docsUrl = (ruleName) => `https://github.com/storybookjs/storybook/blob/next/code/lib/eslint-plugin/docs/rules/${ruleName}.md`; +var getMetaObjectExpression = (node, context) => { + let meta2 = node.declaration, { sourceCode } = context; + if (isIdentifier(meta2)) { + let scope = sourceCode.getScope ? sourceCode.getScope(node) : context.getScope(), variable = ASTUtils.findVariable(scope, meta2.name), decl = variable && variable.defs.find((def) => isVariableDeclarator(def.node)); + decl && isVariableDeclarator(decl.node) && (meta2 = decl.node.init); + } + return (isTSAsExpression(meta2) || isTSSatisfiesExpression(meta2)) && (meta2 = meta2.expression), isObjectExpression(meta2) ? meta2 : null; +}; +var getDescriptor = (metaDeclaration, propertyName) => { + let property = metaDeclaration && metaDeclaration.properties.find( + (p) => "key" in p && "name" in p.key && p.key.name === propertyName + ); + if (!property || isSpreadElement(property)) + return; + let { type } = property.value; + switch (type) { + case "ArrayExpression": + return property.value.elements.map((t) => { + if (t === null) + throw new Error("Unexpected descriptor element: null"); + if (!["StringLiteral", "Literal"].includes(t.type)) + throw new Error(`Unexpected descriptor element: ${t.type}`); + return t.value; + }); + case "Literal": + // @ts-expect-error TODO: Investigation needed. Type systems says, that "RegExpLiteral" does not exist + case "RegExpLiteral": + return property.value.value; + default: + throw new Error(`Unexpected descriptor: ${type}`); + } +}; +var isValidStoryExport = (node, nonStoryExportsConfig) => isExportStory(node.name, nonStoryExportsConfig) && node.name !== "__namedExportsOrder"; +var getAllNamedExports = (node) => { + if (!node.declaration && node.specifiers) + return node.specifiers.reduce((acc, specifier) => (isIdentifier(specifier.exported) && acc.push(specifier.exported), acc), []); + let decl = node.declaration; + if (isVariableDeclaration(decl)) { + let declaration = decl.declarations[0]; + if (declaration) { + let { id } = declaration; + if (isIdentifier(id)) + return [id]; + } + } + return isFunctionDeclaration(decl) && isIdentifier(decl.id) ? [decl.id] : []; +}; +function createStorybookRule({ + create, + meta: meta2, + ...remainingConfig +}) { + return RuleCreator(docsUrl)({ + ...remainingConfig, + create, + meta: { + ...meta2, + docs: { + ...meta2.docs + }, + defaultOptions: remainingConfig.defaultOptions + } + }); +} +var await_interactions_default = createStorybookRule({ + name: "await-interactions", + defaultOptions: [], + meta: { + severity: "error", + docs: { + description: "Interactions should be awaited", + categories: [ + "addon-interactions", + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + interactionShouldBeAwaited: "Interaction should be awaited: {{method}}", + fixSuggestion: "Add `await` to method" + }, + type: "problem", + fixable: "code", + hasSuggestions: true, + schema: [] + }, + create(context) { + let FUNCTIONS_TO_BE_AWAITED = [ + "waitFor", + "waitForElementToBeRemoved", + "wait", + "waitForElement", + "waitForDomChange", + "userEvent", + "play" + ], getMethodThatShouldBeAwaited = (expr) => { + let shouldAwait = (name2) => FUNCTIONS_TO_BE_AWAITED.includes(name2) || name2.startsWith("findBy"); + return isArrowFunctionExpression(expr.parent) || isReturnStatement(expr.parent) ? null : isMemberExpression(expr.callee) && isIdentifier(expr.callee.object) && shouldAwait(expr.callee.object.name) ? expr.callee.object : isTSNonNullExpression(expr.callee) && isMemberExpression(expr.callee.expression) && isIdentifier(expr.callee.expression.property) && shouldAwait(expr.callee.expression.property.name) ? expr.callee.expression.property : isMemberExpression(expr.callee) && isIdentifier(expr.callee.property) && shouldAwait(expr.callee.property.name) || isMemberExpression(expr.callee) && isCallExpression(expr.callee.object) && isIdentifier(expr.callee.object.callee) && isIdentifier(expr.callee.property) && expr.callee.object.callee.name === "expect" ? expr.callee.property : isIdentifier(expr.callee) && shouldAwait(expr.callee.name) ? expr.callee : null; + }, getClosestFunctionAncestor = (node) => { + let parent = node.parent; + if (!(!parent || isProgram(parent))) + return isArrowFunctionExpression(parent) || isFunctionExpression(parent) || isFunctionDeclaration(parent) ? node.parent : getClosestFunctionAncestor(parent); + }, isUserEventFromStorybookImported = (node) => (node.source.value === "@storybook/testing-library" || node.source.value === "@storybook/test") && node.specifiers.find( + (spec) => isImportSpecifier(spec) && "name" in spec.imported && spec.imported.name === "userEvent" && spec.local.name === "userEvent" + ) !== void 0, isExpectFromStorybookImported = (node) => (node.source.value === "@storybook/jest" || node.source.value === "@storybook/test") && node.specifiers.find( + (spec) => isImportSpecifier(spec) && "name" in spec.imported && spec.imported.name === "expect" + ) !== void 0, isImportedFromStorybook = true, invocationsThatShouldBeAwaited = []; + return { + ImportDeclaration(node) { + isImportedFromStorybook = isUserEventFromStorybookImported(node) || isExpectFromStorybookImported(node); + }, + VariableDeclarator(node) { + isImportedFromStorybook = isImportedFromStorybook && isIdentifier(node.id) && node.id.name !== "userEvent"; + }, + CallExpression(node) { + let method = getMethodThatShouldBeAwaited(node); + method && !isAwaitExpression(node.parent) && !isAwaitExpression(node.parent?.parent) && invocationsThatShouldBeAwaited.push({ node, method }); + }, + "Program:exit": function() { + isImportedFromStorybook && invocationsThatShouldBeAwaited.length && invocationsThatShouldBeAwaited.forEach(({ node, method }) => { + let parentFnNode = getClosestFunctionAncestor(node), parentFnNeedsAsync = parentFnNode && !("async" in parentFnNode && parentFnNode.async), fixFn = (fixer) => { + let fixerResult = [fixer.insertTextBefore(node, "await ")]; + return parentFnNeedsAsync && fixerResult.push(fixer.insertTextBefore(parentFnNode, "async ")), fixerResult; + }; + context.report({ + node, + messageId: "interactionShouldBeAwaited", + data: { + method: method.name + }, + fix: fixFn, + suggest: [ + { + messageId: "fixSuggestion", + fix: fixFn + } + ] + }); + }); + } + }; + } +}); +var context_in_play_function_default = createStorybookRule({ + name: "context-in-play-function", + defaultOptions: [], + meta: { + type: "problem", + severity: "error", + docs: { + description: "Pass a context when invoking play function of another story", + categories: [ + "recommended", + "addon-interactions" + /* ADDON_INTERACTIONS */ + ] + }, + messages: { + passContextToPlayFunction: "Pass a context when invoking play function of another story" + }, + fixable: void 0, + schema: [] + }, + create(context) { + let isPlayFunctionFromAnotherStory = (expr) => !!(isTSNonNullExpression(expr.callee) && isMemberExpression(expr.callee.expression) && isIdentifier(expr.callee.expression.property) && expr.callee.expression.property.name === "play" || isMemberExpression(expr.callee) && isIdentifier(expr.callee.property) && expr.callee.property.name === "play"), getParentParameterName = (node) => { + if (!isArrowFunctionExpression(node)) + return node.parent ? getParentParameterName(node.parent) : void 0; + if (node.params.length !== 0 && node.params.length >= 1) { + let param = node.params[0]; + if (isIdentifier(param)) + return param.name; + if (isObjectPattern(param)) { + if (param.properties.find((prop) => prop.type === "Property" && prop.key.type === "Identifier" && prop.key.name === "context")) + return "context"; + let restElement = param.properties.find(isRestElement); + return !restElement || !isIdentifier(restElement.argument) ? void 0 : restElement.argument.name; + } + } + }, isNotPassingContextCorrectly = (expr) => { + let firstExpressionArgument = expr.arguments[0]; + if (!firstExpressionArgument) + return true; + let contextVariableName = getParentParameterName(expr); + return contextVariableName ? !(expr.arguments.length === 1 && isIdentifier(firstExpressionArgument) && firstExpressionArgument.name === contextVariableName || isObjectExpression(firstExpressionArgument) && firstExpressionArgument.properties.some((prop) => isSpreadElement(prop) && isIdentifier(prop.argument) && prop.argument.name === contextVariableName)) : true; + }, invocationsWithoutProperContext = []; + return { + CallExpression(node) { + isPlayFunctionFromAnotherStory(node) && isNotPassingContextCorrectly(node) && invocationsWithoutProperContext.push(node); + }, + "Program:exit": function() { + invocationsWithoutProperContext.forEach((node) => { + context.report({ + node, + messageId: "passContextToPlayFunction" + }); + }); + } + }; + } +}); +var csf_component_default = createStorybookRule({ + name: "csf-component", + defaultOptions: [], + meta: { + type: "suggestion", + severity: "warn", + docs: { + description: "The component property should be set", + categories: [ + "csf" + /* CSF */ + ] + }, + messages: { + missingComponentProperty: "Missing component property." + }, + schema: [] + }, + create(context) { + return { + ExportDefaultDeclaration(node) { + let meta2 = getMetaObjectExpression(node, context); + if (!meta2) + return null; + meta2.properties.find( + (property) => !isSpreadElement(property) && "name" in property.key && property.key.name === "component" + ) || context.report({ + node, + messageId: "missingComponentProperty" + }); + } + }; + } +}); +var default_exports_default = createStorybookRule({ + name: "default-exports", + defaultOptions: [], + meta: { + type: "problem", + severity: "error", + docs: { + description: "Story files should have a default export", + categories: [ + "csf", + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + shouldHaveDefaultExport: "The file should have a default export.", + fixSuggestion: "Add default export" + }, + fixable: "code", + hasSuggestions: true, + schema: [] + }, + create(context) { + let getComponentName = (node, filePath) => { + let name2 = path.basename(filePath).split(".")[0]; + return node.body.find((stmt) => { + if (isImportDeclaration(stmt) && isLiteral(stmt.source) && stmt.source.value.startsWith(`./${name2}`)) + return !!stmt.specifiers.find( + (spec) => isIdentifier(spec.local) && spec.local.name === name2 + ); + }) ? name2 : null; + }, hasDefaultExport = false, isCsf4Style = false, hasStoriesOfImport = false; + return { + ImportSpecifier(node) { + "name" in node.imported && node.imported.name === "storiesOf" && (hasStoriesOfImport = true); + }, + VariableDeclaration(node) { + node.parent.type === "Program" && node.declarations.forEach((declaration) => { + let init = declaration.init; + if (init && init.type === "CallExpression") { + let callee = init.callee; + callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === "meta" && (isCsf4Style = true); + } + }); + }, + ExportDefaultSpecifier: function() { + hasDefaultExport = true; + }, + ExportDefaultDeclaration: function() { + hasDefaultExport = true; + }, + "Program:exit": function(program) { + if (!isCsf4Style && !hasDefaultExport && !hasStoriesOfImport) { + let componentName = getComponentName(program, context.filename), node = program.body.find((n) => !isImportDeclaration(n)) || program.body[0] || program, report = { + node, + messageId: "shouldHaveDefaultExport" + }, fix = (fixer) => { + let metaDeclaration = componentName ? `export default { component: ${componentName} } +` : `export default {} +`; + return fixer.insertTextBefore(node, metaDeclaration); + }; + context.report({ + ...report, + fix, + suggest: [ + { + messageId: "fixSuggestion", + fix + } + ] + }); + } + } + }; + } +}); +var hierarchy_separator_default = createStorybookRule({ + name: "hierarchy-separator", + defaultOptions: [], + meta: { + type: "problem", + fixable: "code", + hasSuggestions: true, + severity: "warn", + docs: { + description: "Deprecated hierarchy separator in title property", + categories: [ + "csf", + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + useCorrectSeparators: "Use correct separators", + deprecatedHierarchySeparator: "Deprecated hierarchy separator in title property: {{metaTitle}}." + }, + schema: [] + }, + create: function(context) { + return { + ExportDefaultDeclaration: function(node) { + let meta2 = getMetaObjectExpression(node, context); + if (!meta2) + return null; + let titleNode = meta2.properties.find( + (prop) => !isSpreadElement(prop) && "name" in prop.key && prop.key?.name === "title" + ); + if (!titleNode || !isLiteral(titleNode.value)) + return; + let metaTitle = titleNode.value.raw || ""; + metaTitle.includes("|") && context.report({ + node: titleNode, + messageId: "deprecatedHierarchySeparator", + data: { metaTitle }, + // In case we want this to be auto fixed by --fix + fix: function(fixer) { + return fixer.replaceTextRange(titleNode.value.range, metaTitle.replace(/\|/g, "/")); + }, + suggest: [ + { + messageId: "useCorrectSeparators", + fix: function(fixer) { + return fixer.replaceTextRange( + titleNode.value.range, + metaTitle.replace(/\|/g, "/") + ); + } + } + ] + }); + } + }; + } +}); +var meta_inline_properties_default = createStorybookRule({ + name: "meta-inline-properties", + defaultOptions: [{ csfVersion: 3 }], + meta: { + type: "problem", + severity: "error", + docs: { + description: "Meta should only have inline properties", + categories: [ + "csf", + "recommended" + /* RECOMMENDED */ + ], + excludeFromConfig: true + }, + messages: { + metaShouldHaveInlineProperties: "Meta should only have inline properties: {{property}}" + }, + schema: [ + { + type: "object", + properties: { + csfVersion: { + type: "number" + } + }, + additionalProperties: false + } + ] + }, + create(context) { + let isInline = (node) => node && typeof node == "object" && "value" in node ? node.value.type === "ObjectExpression" || node.value.type === "Literal" || node.value.type === "ArrayExpression" : false; + return { + ExportDefaultDeclaration(node) { + let meta2 = getMetaObjectExpression(node, context); + if (!meta2) + return null; + let ruleProperties = ["title", "args"], dynamicProperties = []; + meta2.properties.filter( + (prop) => "key" in prop && "name" in prop.key && ruleProperties.includes(prop.key.name) + ).forEach((metaNode) => { + isInline(metaNode) || dynamicProperties.push(metaNode); + }), dynamicProperties.length > 0 && dynamicProperties.forEach((propertyNode) => { + context.report({ + node: propertyNode, + messageId: "metaShouldHaveInlineProperties", + data: { + property: propertyNode.key?.name + } + }); + }); + } + }; + } +}); +var meta_satisfies_type_default = createStorybookRule({ + name: "meta-satisfies-type", + defaultOptions: [], + meta: { + type: "problem", + fixable: "code", + severity: "error", + docs: { + description: "Meta should use `satisfies Meta`", + categories: [], + excludeFromConfig: true + }, + messages: { + metaShouldSatisfyType: "CSF Meta should use `satisfies` for type safety" + }, + schema: [] + }, + create(context) { + let sourceCode = context.sourceCode, getTextWithParentheses = (node) => { + let beforeCount = 0, afterCount = 0; + if (ASTUtils.isParenthesized(node, sourceCode)) { + let bodyOpeningParen = sourceCode.getTokenBefore(node, ASTUtils.isOpeningParenToken), bodyClosingParen = sourceCode.getTokenAfter(node, ASTUtils.isClosingParenToken); + bodyOpeningParen && bodyClosingParen && (beforeCount = node.range[0] - bodyOpeningParen.range[0], afterCount = bodyClosingParen.range[1] - node.range[1]); + } + return sourceCode.getText(node, beforeCount, afterCount); + }, getFixer = (meta2) => { + let { parent } = meta2; + if (parent) + switch (parent.type) { + // {} as Meta + case AST_NODE_TYPES2.TSAsExpression: + return (fixer) => [ + fixer.replaceText(parent, getTextWithParentheses(meta2)), + fixer.insertTextAfter( + parent, + ` satisfies ${getTextWithParentheses(parent.typeAnnotation)}` + ) + ]; + // const meta: Meta = {} + case AST_NODE_TYPES2.VariableDeclarator: { + let { typeAnnotation } = parent.id; + return typeAnnotation ? (fixer) => [ + fixer.remove(typeAnnotation), + fixer.insertTextAfter( + meta2, + ` satisfies ${getTextWithParentheses(typeAnnotation.typeAnnotation)}` + ) + ] : void 0; + } + default: + return; + } + }; + return { + ExportDefaultDeclaration(node) { + let meta2 = getMetaObjectExpression(node, context); + if (!meta2) + return null; + (!meta2.parent || !isTSSatisfiesExpression(meta2.parent)) && context.report({ + node: meta2, + messageId: "metaShouldSatisfyType", + fix: getFixer(meta2) + }); + } + }; + } +}); +var no_redundant_story_name_default = createStorybookRule({ + name: "no-redundant-story-name", + defaultOptions: [], + meta: { + type: "suggestion", + fixable: "code", + hasSuggestions: true, + severity: "warn", + docs: { + description: "A story should not have a redundant name property", + categories: [ + "csf", + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + removeRedundantName: "Remove redundant name", + storyNameIsRedundant: "Named exports should not use the name annotation if it is redundant to the name that would be generated by the export name" + }, + schema: [] + }, + create(context) { + return { + // CSF3 + ExportNamedDeclaration: function(node) { + if (!node.declaration) + return; + let decl = node.declaration; + if (isVariableDeclaration(decl)) { + let declaration = decl.declarations[0]; + if (declaration == null) + return; + let { id, init } = declaration; + if (isIdentifier(id) && isObjectExpression(init)) { + let storyNameNode = init.properties.find( + (prop) => isProperty(prop) && isIdentifier(prop.key) && (prop.key?.name === "name" || prop.key?.name === "storyName") + ); + if (!storyNameNode) + return; + let { name: name2 } = id, resolvedStoryName = storyNameFromExport(name2); + !isSpreadElement(storyNameNode) && isLiteral(storyNameNode.value) && storyNameNode.value.value === resolvedStoryName && context.report({ + node: storyNameNode, + messageId: "storyNameIsRedundant", + suggest: [ + { + messageId: "removeRedundantName", + fix: function(fixer) { + return fixer.remove(storyNameNode); + } + } + ] + }); + } + } + }, + // CSF2 + AssignmentExpression: function(node) { + if (!isExpressionStatement(node.parent)) + return; + let { left, right } = node; + if ("property" in left && isIdentifier(left.property) && !isMetaProperty(left) && left.property.name === "storyName") { + if (!("name" in left.object && "value" in right)) + return; + let propertyName = left.object.name, propertyValue = right.value, resolvedStoryName = storyNameFromExport(propertyName); + propertyValue === resolvedStoryName && context.report({ + node, + messageId: "storyNameIsRedundant", + suggest: [ + { + messageId: "removeRedundantName", + fix: function(fixer) { + return fixer.remove(node); + } + } + ] + }); + } + } + }; + } +}); +var rendererToFrameworks = { + "@storybook/html": ["@storybook/html-vite", "@storybook/html-webpack5"], + "@storybook/preact": ["@storybook/preact-vite", "@storybook/preact-webpack5"], + "@storybook/react": [ + "@storybook/nextjs", + "@storybook/react-vite", + "@storybook/nextjs-vite", + "@storybook/react-webpack5", + "@storybook/react-native-web-vite" + ], + "@storybook/server": ["@storybook/server-webpack5"], + "@storybook/svelte": [ + "@storybook/svelte-vite", + "@storybook/svelte-webpack5", + "@storybook/sveltekit" + ], + "@storybook/vue3": ["@storybook/vue3-vite", "@storybook/vue3-webpack5"], + "@storybook/web-components": [ + "@storybook/web-components-vite", + "@storybook/web-components-webpack5" + ] +}; +var no_renderer_packages_default = createStorybookRule({ + name: "no-renderer-packages", + defaultOptions: [], + meta: { + type: "problem", + severity: "error", + docs: { + description: "Do not import renderer packages directly in stories", + categories: [ + "recommended" + /* RECOMMENDED */ + ] + }, + schema: [], + messages: { + noRendererPackages: 'Do not import renderer package "{{rendererPackage}}" directly. Use a framework package instead (e.g. {{suggestions}}).' + } + }, + create(context) { + return { + ImportDeclaration(node) { + let packageName = node.source.value; + if (typeof packageName == "string" && packageName in rendererToFrameworks) { + let suggestions = rendererToFrameworks[packageName]; + context.report({ + node, + messageId: "noRendererPackages", + data: { + rendererPackage: packageName, + suggestions: suggestions.join(", ") + } + }); + } + } + }; + } +}); +var no_stories_of_default = createStorybookRule({ + name: "no-stories-of", + defaultOptions: [], + meta: { + type: "problem", + severity: "error", + docs: { + description: "storiesOf is deprecated and should not be used", + categories: [ + "csf-strict" + /* CSF_STRICT */ + ] + }, + messages: { + doNotUseStoriesOf: "storiesOf is deprecated and should not be used" + }, + schema: [] + }, + create(context) { + return { + ImportSpecifier(node) { + "name" in node.imported && node.imported.name === "storiesOf" && context.report({ + node, + messageId: "doNotUseStoriesOf" + }); + } + }; + } +}); +var no_title_property_in_meta_default = createStorybookRule({ + name: "no-title-property-in-meta", + defaultOptions: [], + meta: { + type: "problem", + fixable: "code", + hasSuggestions: true, + severity: "error", + docs: { + description: "Do not define a title in meta", + categories: [ + "csf-strict" + /* CSF_STRICT */ + ] + }, + messages: { + removeTitleInMeta: "Remove title property from meta", + noTitleInMeta: "CSF3 does not need a title in meta" + }, + schema: [] + }, + create: function(context) { + return { + ExportDefaultDeclaration: function(node) { + let meta2 = getMetaObjectExpression(node, context); + if (!meta2) + return null; + let titleNode = meta2.properties.find( + (prop) => !isSpreadElement(prop) && "name" in prop.key && prop.key?.name === "title" + ); + titleNode && context.report({ + node: titleNode, + messageId: "noTitleInMeta", + suggest: [ + { + messageId: "removeTitleInMeta", + fix(fixer) { + let hasComma = context.sourceCode.text.slice( + titleNode.range[0], + titleNode.range[1] + 1 + ).slice(-1) === ",", propertyRange = [ + titleNode.range[0], + hasComma ? titleNode.range[1] + 1 : titleNode.range[1] + ]; + return fixer.removeRange(propertyRange); + } + } + ] + }); + } + }; + } +}); +var import_ts_dedent = __toESM(require_dist(), 1); +var no_uninstalled_addons_default = createStorybookRule({ + name: "no-uninstalled-addons", + defaultOptions: [ + { + packageJsonLocation: "", + ignore: [] + } + ], + meta: { + type: "problem", + severity: "error", + docs: { + description: "This rule identifies storybook addons that are invalid because they are either not installed or contain a typo in their name.", + categories: [ + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + addonIsNotInstalled: "The {{ addonName }} is not installed in {{packageJsonPath}}. Did you forget to install it or is your package.json in a different location?" + }, + schema: [ + { + type: "object", + properties: { + packageJsonLocation: { + type: "string" + }, + ignore: { + type: "array", + items: { + type: "string" + } + } + }, + additionalProperties: false + } + ] + }, + create(context) { + let { packageJsonLocation, ignore } = context.options.reduce( + (acc, val) => ({ + packageJsonLocation: val.packageJsonLocation || acc.packageJsonLocation, + ignore: val.ignore || acc.ignore + }), + { packageJsonLocation: "", ignore: [] } + ); + function excludeNullable(item) { + return !!item; + } + let mergeDepsWithDevDeps = (packageJson) => { + let deps = Object.keys(packageJson.dependencies || {}), devDeps = Object.keys(packageJson.devDependencies || {}); + return [...deps, ...devDeps]; + }, isAddonInstalled = (addon, installedAddons) => { + let addonName = addon.replace(/\.[mc]?js$/, "").replace(/\/register$/, "").replace(/\/preset$/, ""); + return installedAddons.includes(addonName); + }, filterLocalAddons = (addon) => !((addonName) => addonName.startsWith(".") || addonName.startsWith("/") || // for local Windows files e.g. (C: F: D:) + /\w:.*/.test(addonName) || addonName.startsWith("\\"))(addon), areThereAddonsNotInstalled = (addons, installedSbAddons) => { + let result = addons.filter(filterLocalAddons).filter((addon) => !isAddonInstalled(addon, installedSbAddons) && !ignore.includes(addon)).map((addon) => ({ name: addon })); + return result.length ? result : false; + }, getPackageJson = (path2) => { + let packageJson = { + devDependencies: {}, + dependencies: {} + }; + try { + let file = readFileSync(path2, "utf8"), parsedFile = JSON.parse(file); + packageJson.dependencies = parsedFile.dependencies || {}, packageJson.devDependencies = parsedFile.devDependencies || {}; + } catch { + throw new Error( + import_ts_dedent.dedent`The provided path in your eslintrc.json - ${path2} is not a valid path to a package.json file or your package.json file is not in the same folder as ESLint is running from. + + Read more at: https://github.com/storybookjs/storybook/blob/next/code/lib/eslint-plugin/docs/rules/no-uninstalled-addons.md + ` + ); + } + return packageJson; + }, extractAllAddonsFromTheStorybookConfig = (addonsExpression) => { + if (addonsExpression?.elements) { + let nodesWithAddons = addonsExpression.elements.map((elem) => isLiteral(elem) ? { value: elem.value, node: elem } : void 0).filter(excludeNullable), listOfAddonsInString = nodesWithAddons.map((elem) => elem.value), nodesWithAddonsInObj = addonsExpression.elements.map((elem) => isObjectExpression(elem) ? elem : { properties: [] }).map((elem) => { + let property = elem.properties.find( + (prop) => isProperty(prop) && isIdentifier(prop.key) && prop.key.name === "name" + ); + return isLiteral(property?.value) ? { value: property.value.value, node: property.value } : void 0; + }).filter(excludeNullable), listOfAddonsInObj = nodesWithAddonsInObj.map((elem) => elem.value), listOfAddons = [...listOfAddonsInString, ...listOfAddonsInObj], listOfAddonElements = [...nodesWithAddons, ...nodesWithAddonsInObj]; + return { listOfAddons, listOfAddonElements }; + } + return { listOfAddons: [], listOfAddonElements: [] }; + }; + function reportUninstalledAddons(addonsProp) { + let packageJsonPath = resolve(packageJsonLocation || "./package.json"), packageJsonObject; + try { + packageJsonObject = getPackageJson(packageJsonPath); + } catch (e) { + throw new Error(e); + } + let depsAndDevDeps = mergeDepsWithDevDeps(packageJsonObject), { listOfAddons, listOfAddonElements } = extractAllAddonsFromTheStorybookConfig(addonsProp), result = areThereAddonsNotInstalled(listOfAddons, depsAndDevDeps); + if (result) { + let elemsWithErrors = listOfAddonElements.filter( + (elem) => !!result.find((addon) => addon.name === elem.value) + ), currentPackageJsonPath = `${process.cwd().split(sep).pop()}${sep}${relative(process.cwd(), packageJsonLocation)}`; + elemsWithErrors.forEach((elem) => { + context.report({ + node: elem.node, + messageId: "addonIsNotInstalled", + data: { + addonName: elem.value, + packageJsonPath: currentPackageJsonPath + } + }); + }); + } + } + function findAddonsPropAndReport(node) { + let addonsProp = node.properties.find( + (prop) => isProperty(prop) && isIdentifier(prop.key) && prop.key.name === "addons" + ); + addonsProp?.value && isArrayExpression(addonsProp.value) && reportUninstalledAddons(addonsProp.value); + } + return { + AssignmentExpression: function(node) { + isObjectExpression(node.right) && findAddonsPropAndReport(node.right); + }, + ExportDefaultDeclaration: function(node) { + let meta2 = getMetaObjectExpression(node, context); + if (!meta2) + return null; + findAddonsPropAndReport(meta2); + }, + ExportNamedDeclaration: function(node) { + let addonsProp = isVariableDeclaration(node.declaration) && node.declaration.declarations.find( + (decl) => isVariableDeclarator(decl) && isIdentifier(decl.id) && decl.id.name === "addons" + ); + addonsProp && isArrayExpression(addonsProp.init) && reportUninstalledAddons(addonsProp.init); + } + }; + } +}); +var prefer_pascal_case_default = createStorybookRule({ + name: "prefer-pascal-case", + defaultOptions: [], + meta: { + type: "suggestion", + fixable: "code", + hasSuggestions: true, + severity: "warn", + docs: { + description: "Stories should use PascalCase", + categories: [ + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + convertToPascalCase: "Use pascal case", + usePascalCase: "The story should use PascalCase notation: {{name}}" + }, + schema: [] + }, + create(context) { + let isPascalCase = (str) => /^[A-Z]+([a-z0-9]?)+/.test(str), toPascalCase = (str) => str.replace(new RegExp(/[-_]+/, "g"), " ").replace(new RegExp(/[^\w\s]/, "g"), "").replace( + new RegExp(/\s+(.)(\w+)/, "g"), + (_, $2, $3) => `${$2.toUpperCase() + $3.toLowerCase()}` + ).replace(new RegExp(/\s/, "g"), "").replace(new RegExp(/\w/), (s) => s.toUpperCase()), getModuleScope = () => { + let { sourceCode } = context; + return sourceCode.getScope ? sourceCode.scopeManager?.scopes?.find( + (scope) => scope.type === "module" + ) : context.getScope().childScopes.find((scope) => scope.type === "module"); + }, checkAndReportError = (id, nonStoryExportsConfig2 = {}) => { + let { name: name2 } = id; + if (!isExportStory(name2, nonStoryExportsConfig2) || name2 === "__namedExportsOrder") + return null; + !name2.startsWith("_") && !isPascalCase(name2) && context.report({ + node: id, + messageId: "usePascalCase", + data: { + name: name2 + }, + suggest: [ + { + messageId: "convertToPascalCase", + *fix(fixer) { + let suffix = context.sourceCode.text.slice(id.range[0], id.range[1]).substring(name2.length), pascal = toPascalCase(name2); + yield fixer.replaceTextRange(id.range, pascal + suffix); + let scope = getModuleScope(); + if (scope) { + let variable = ASTUtils.findVariable(scope, name2), referenceCount = variable?.references?.length || 0; + for (let i = 0; i < referenceCount; i++) { + let ref = variable?.references[i]; + ref && !ref.init && (yield fixer.replaceTextRange(ref.identifier.range, pascal)); + } + } + } + } + ] + }); + }, meta2, nonStoryExportsConfig, namedExports = [], hasStoriesOfImport = false; + return { + ImportSpecifier(node) { + "name" in node.imported && node.imported.name === "storiesOf" && (hasStoriesOfImport = true); + }, + ExportDefaultDeclaration: function(node) { + if (meta2 = getMetaObjectExpression(node, context), meta2) + try { + nonStoryExportsConfig = { + excludeStories: getDescriptor(meta2, "excludeStories"), + includeStories: getDescriptor(meta2, "includeStories") + }; + } catch { + } + }, + ExportNamedDeclaration: function(node) { + if (!node.declaration) + return; + let decl = node.declaration; + if (isVariableDeclaration(decl)) { + let declaration = decl.declarations[0]; + if (declaration == null) + return; + let { id } = declaration; + isIdentifier(id) && namedExports.push(id); + } + }, + "Program:exit": function() { + namedExports.length && !hasStoriesOfImport && namedExports.forEach((n) => checkAndReportError(n, nonStoryExportsConfig)); + } + }; + } +}); +var story_exports_default = createStorybookRule({ + name: "story-exports", + defaultOptions: [], + meta: { + type: "problem", + severity: "error", + docs: { + description: "A story file must contain at least one story export", + categories: [ + "recommended", + "csf" + /* CSF */ + ] + }, + messages: { + shouldHaveStoryExport: "The file should have at least one story export", + shouldHaveStoryExportWithFilters: "The file should have at least one story export. Make sure the includeStories/excludeStories you defined are correct, otherwise Storybook will not use any stories for this file.", + addStoryExport: "Add a story export" + }, + fixable: void 0, + // change to 'code' once we have autofixes + schema: [] + }, + create(context) { + let hasStoriesOfImport = false, nonStoryExportsConfig = {}, meta2, namedExports = []; + return { + ImportSpecifier(node) { + "name" in node.imported && node.imported.name === "storiesOf" && (hasStoriesOfImport = true); + }, + ExportDefaultDeclaration: function(node) { + if (meta2 = getMetaObjectExpression(node, context), meta2) + try { + nonStoryExportsConfig = { + excludeStories: getDescriptor(meta2, "excludeStories"), + includeStories: getDescriptor(meta2, "includeStories") + }; + } catch { + } + }, + ExportNamedDeclaration: function(node) { + namedExports.push(...getAllNamedExports(node)); + }, + "Program:exit": function(program) { + if (hasStoriesOfImport || !meta2 || namedExports.filter( + (exp) => isValidStoryExport(exp, nonStoryExportsConfig) + ).length) + return; + let node = program.body.find((n) => !isImportDeclaration(n)) || program.body[0] || program, hasFilter = nonStoryExportsConfig.includeStories || nonStoryExportsConfig.excludeStories, report = { + node, + messageId: hasFilter ? "shouldHaveStoryExportWithFilters" : "shouldHaveStoryExport" + // fix, + }; + context.report(report); + } + }; + } +}); +var use_storybook_expect_default = createStorybookRule({ + name: "use-storybook-expect", + defaultOptions: [], + meta: { + type: "suggestion", + fixable: "code", + schema: [], + severity: "error", + docs: { + description: "Use expect from `@storybook/test`, `storybook/test` or `@storybook/jest`", + categories: [ + "addon-interactions", + "recommended" + /* RECOMMENDED */ + ] + }, + messages: { + useExpectFromStorybook: "Do not use global expect directly in the story. You should import it from `@storybook/test` (preferrably) or `@storybook/jest` instead." + } + }, + create(context) { + let isExpectFromStorybookImported = (node) => { + let { value: packageName } = node.source; + return (packageName === "@storybook/jest" || packageName === "@storybook/test" || packageName === "storybook/test") && node.specifiers.find( + (spec) => isImportSpecifier(spec) && "name" in spec.imported && spec.imported.name === "expect" + ); + }, isImportingFromStorybookExpect = false, expectInvocations = []; + return { + ImportDeclaration(node) { + isExpectFromStorybookImported(node) && (isImportingFromStorybookExpect = true); + }, + CallExpression(node) { + if (!isIdentifier(node.callee)) + return null; + node.callee.name === "expect" && expectInvocations.push(node.callee); + }, + "Program:exit": function() { + !isImportingFromStorybookExpect && expectInvocations.length && expectInvocations.forEach((node) => { + context.report({ + node, + messageId: "useExpectFromStorybook" + }); + }); + } + }; + } +}); +var use_storybook_testing_library_default = createStorybookRule({ + name: "use-storybook-testing-library", + defaultOptions: [], + meta: { + type: "suggestion", + fixable: "code", + hasSuggestions: true, + severity: "error", + docs: { + description: "Do not use testing-library directly on stories", + categories: [ + "addon-interactions", + "recommended" + /* RECOMMENDED */ + ] + }, + schema: [], + messages: { + updateImports: "Update imports", + dontUseTestingLibraryDirectly: "Do not use `{{library}}` directly in the story. You should import the functions from `storybook/test` instead." + } + }, + create(context) { + let getRangeWithoutQuotes = (source) => [ + // Not sure how to improve this. If I use node.source.range + // it will eat the quotes and we do not want to specify whether the quotes are single or double + source.range[0] + 1, + source.range[1] - 1 + ], hasDefaultImport = (specifiers) => specifiers.find((s) => isImportDefaultSpecifier(s)), getSpecifiers = (node) => { + let { specifiers } = node; + if (!specifiers[0]) + return null; + let start = specifiers[0].range[0], previousSpecifier = specifiers[specifiers.length - 1]; + if (!previousSpecifier) + return null; + let end = previousSpecifier.range[1], fullText = context.sourceCode.text, importEnd = node.range[1], closingBrace = fullText.indexOf("}", end - 1); + closingBrace > -1 && closingBrace <= importEnd && (end = closingBrace + 1); + let text = fullText.substring(start, end); + return { range: [start, end], text }; + }, fixSpecifiers = (specifiersText) => `{ ${specifiersText.replace("{", "").replace("}", "").replace(/\s\s+/g, " ").trim()} }`; + return { + ImportDeclaration(node) { + node.source.value.includes("@testing-library") && context.report({ + node, + messageId: "dontUseTestingLibraryDirectly", + data: { + library: node.source.value + }, + *fix(fixer) { + if (yield fixer.replaceTextRange(getRangeWithoutQuotes(node.source), "storybook/test"), hasDefaultImport(node.specifiers)) { + let specifiers = getSpecifiers(node); + if (specifiers) { + let { range, text } = specifiers; + yield fixer.replaceTextRange(range, fixSpecifiers(text)); + } + } + }, + suggest: [ + { + messageId: "updateImports", + *fix(fixer) { + if (yield fixer.replaceTextRange( + getRangeWithoutQuotes(node.source), + "storybook/test" + ), hasDefaultImport(node.specifiers)) { + let specifiers = getSpecifiers(node); + if (specifiers) { + let { range, text } = specifiers; + yield fixer.replaceTextRange(range, fixSpecifiers(text)); + } + } + } + } + ] + }); + } + }; + } +}); +var configs = { + // eslintrc configs + csf: csf_default, + "csf-strict": csf_strict_default, + "addon-interactions": addon_interactions_default, + recommended: recommended_default, + // flat configs + "flat/csf": csf_default2, + "flat/csf-strict": csf_strict_default2, + "flat/addon-interactions": addon_interactions_default2, + "flat/recommended": recommended_default2 +}; +var rules = { + "await-interactions": await_interactions_default, + "context-in-play-function": context_in_play_function_default, + "csf-component": csf_component_default, + "default-exports": default_exports_default, + "hierarchy-separator": hierarchy_separator_default, + "meta-inline-properties": meta_inline_properties_default, + "meta-satisfies-type": meta_satisfies_type_default, + "no-redundant-story-name": no_redundant_story_name_default, + "no-renderer-packages": no_renderer_packages_default, + "no-stories-of": no_stories_of_default, + "no-title-property-in-meta": no_title_property_in_meta_default, + "no-uninstalled-addons": no_uninstalled_addons_default, + "prefer-pascal-case": prefer_pascal_case_default, + "story-exports": story_exports_default, + "use-storybook-expect": use_storybook_expect_default, + "use-storybook-testing-library": use_storybook_testing_library_default +}; +var meta = { + name, + version +}; +var index_default = { + configs, + meta, + rules +}; +export { + index_default as default +}; diff --git a/config/oxlint/vendor/syntax-rule-utilities.mjs b/config/oxlint/vendor/syntax-rule-utilities.mjs new file mode 100644 index 000000000000..8d67f7e1c44a --- /dev/null +++ b/config/oxlint/vendor/syntax-rule-utilities.mjs @@ -0,0 +1,639 @@ +/** Generated by build.mjs. See README.md for source versions, licenses and supported naming selectors. */ +import {createRequire as createVendorRequire} from 'node:module'; +const require = createVendorRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// @typescript-eslint/utils/dist/ast-utils/eslint-utils/astUtilities.js +var require_astUtilities = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/eslint-utils/astUtilities.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isParenthesized = exports.hasSideEffect = exports.getStringIfConstant = exports.getStaticValue = exports.getPropertyName = exports.getFunctionNameWithKind = exports.getFunctionHeadLocation = void 0; + var eslintUtils = __importStar(__require("@eslint-community/eslint-utils")); + exports.getFunctionHeadLocation = eslintUtils.getFunctionHeadLocation; + exports.getFunctionNameWithKind = eslintUtils.getFunctionNameWithKind; + exports.getPropertyName = eslintUtils.getPropertyName; + exports.getStaticValue = eslintUtils.getStaticValue; + exports.getStringIfConstant = eslintUtils.getStringIfConstant; + exports.hasSideEffect = eslintUtils.hasSideEffect; + exports.isParenthesized = eslintUtils.isParenthesized; + } +}); + +// @typescript-eslint/utils/dist/ast-utils/eslint-utils/PatternMatcher.js +var require_PatternMatcher = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/eslint-utils/PatternMatcher.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + exports.PatternMatcher = void 0; + var eslintUtils = __importStar(__require("@eslint-community/eslint-utils")); + exports.PatternMatcher = eslintUtils.PatternMatcher; + } +}); + +// @typescript-eslint/utils/dist/ast-utils/eslint-utils/predicates.js +var require_predicates = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/eslint-utils/predicates.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isNotSemicolonToken = exports.isSemicolonToken = exports.isNotOpeningParenToken = exports.isOpeningParenToken = exports.isNotOpeningBracketToken = exports.isOpeningBracketToken = exports.isNotOpeningBraceToken = exports.isOpeningBraceToken = exports.isNotCommentToken = exports.isCommentToken = exports.isNotCommaToken = exports.isCommaToken = exports.isNotColonToken = exports.isColonToken = exports.isNotClosingParenToken = exports.isClosingParenToken = exports.isNotClosingBracketToken = exports.isClosingBracketToken = exports.isNotClosingBraceToken = exports.isClosingBraceToken = exports.isNotArrowToken = exports.isArrowToken = void 0; + var eslintUtils = __importStar(__require("@eslint-community/eslint-utils")); + exports.isArrowToken = eslintUtils.isArrowToken; + exports.isNotArrowToken = eslintUtils.isNotArrowToken; + exports.isClosingBraceToken = eslintUtils.isClosingBraceToken; + exports.isNotClosingBraceToken = eslintUtils.isNotClosingBraceToken; + exports.isClosingBracketToken = eslintUtils.isClosingBracketToken; + exports.isNotClosingBracketToken = eslintUtils.isNotClosingBracketToken; + exports.isClosingParenToken = eslintUtils.isClosingParenToken; + exports.isNotClosingParenToken = eslintUtils.isNotClosingParenToken; + exports.isColonToken = eslintUtils.isColonToken; + exports.isNotColonToken = eslintUtils.isNotColonToken; + exports.isCommaToken = eslintUtils.isCommaToken; + exports.isNotCommaToken = eslintUtils.isNotCommaToken; + exports.isCommentToken = eslintUtils.isCommentToken; + exports.isNotCommentToken = eslintUtils.isNotCommentToken; + exports.isOpeningBraceToken = eslintUtils.isOpeningBraceToken; + exports.isNotOpeningBraceToken = eslintUtils.isNotOpeningBraceToken; + exports.isOpeningBracketToken = eslintUtils.isOpeningBracketToken; + exports.isNotOpeningBracketToken = eslintUtils.isNotOpeningBracketToken; + exports.isOpeningParenToken = eslintUtils.isOpeningParenToken; + exports.isNotOpeningParenToken = eslintUtils.isNotOpeningParenToken; + exports.isSemicolonToken = eslintUtils.isSemicolonToken; + exports.isNotSemicolonToken = eslintUtils.isNotSemicolonToken; + } +}); + +// @typescript-eslint/utils/dist/ast-utils/eslint-utils/ReferenceTracker.js +var require_ReferenceTracker = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/eslint-utils/ReferenceTracker.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + exports.ReferenceTracker = void 0; + var eslintUtils = __importStar(__require("@eslint-community/eslint-utils")); + var ReferenceTrackerREAD = eslintUtils.ReferenceTracker.READ; + var ReferenceTrackerCALL = eslintUtils.ReferenceTracker.CALL; + var ReferenceTrackerCONSTRUCT = eslintUtils.ReferenceTracker.CONSTRUCT; + var ReferenceTrackerESM = eslintUtils.ReferenceTracker.ESM; + exports.ReferenceTracker = eslintUtils.ReferenceTracker; + } +}); + +// @typescript-eslint/utils/dist/ast-utils/eslint-utils/scopeAnalysis.js +var require_scopeAnalysis = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/eslint-utils/scopeAnalysis.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); + }) : function(o, v) { + o["default"] = v; + }); + var __importStar = exports && exports.__importStar || /* @__PURE__ */ (function() { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function(o2) { + var ar = []; + for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function(mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) { + for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + } + __setModuleDefault(result, mod); + return result; + }; + })(); + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getInnermostScope = exports.findVariable = void 0; + var eslintUtils = __importStar(__require("@eslint-community/eslint-utils")); + exports.findVariable = eslintUtils.findVariable; + exports.getInnermostScope = eslintUtils.getInnermostScope; + } +}); + +// @typescript-eslint/utils/dist/ast-utils/eslint-utils/index.js +var require_eslint_utils = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/eslint-utils/index.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __exportStar = exports && exports.__exportStar || function(m, exports2) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + __exportStar(require_astUtilities(), exports); + __exportStar(require_PatternMatcher(), exports); + __exportStar(require_predicates(), exports); + __exportStar(require_ReferenceTracker(), exports); + __exportStar(require_scopeAnalysis(), exports); + } +}); + +// @typescript-eslint/utils/dist/ast-utils/helpers.js +var require_helpers = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/helpers.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isNotTokenOfTypeWithConditions = exports.isTokenOfTypeWithConditions = exports.isNodeOfTypeWithConditions = exports.isNodeOfTypes = exports.isNodeOfType = void 0; + var isNodeOfType = (nodeType) => (node) => node?.type === nodeType; + exports.isNodeOfType = isNodeOfType; + var isNodeOfTypes = (nodeTypes) => (node) => !!node && nodeTypes.includes(node.type); + exports.isNodeOfTypes = isNodeOfTypes; + var isNodeOfTypeWithConditions = (nodeType, conditions) => { + const entries = Object.entries(conditions); + return (node) => node?.type === nodeType && entries.every(([key, value]) => node[key] === value); + }; + exports.isNodeOfTypeWithConditions = isNodeOfTypeWithConditions; + var isTokenOfTypeWithConditions = (tokenType, conditions) => { + const entries = Object.entries(conditions); + return (token) => token?.type === tokenType && entries.every(([key, value]) => token[key] === value); + }; + exports.isTokenOfTypeWithConditions = isTokenOfTypeWithConditions; + var isNotTokenOfTypeWithConditions = (tokenType, conditions) => (token) => !(0, exports.isTokenOfTypeWithConditions)(tokenType, conditions)(token); + exports.isNotTokenOfTypeWithConditions = isNotTokenOfTypeWithConditions; + } +}); + +// @typescript-eslint/utils/dist/ast-utils/misc.js +var require_misc = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/misc.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.LINEBREAK_MATCHER = void 0; + exports.isTokenOnSameLine = isTokenOnSameLine; + exports.LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/; + function isTokenOnSameLine(left, right) { + return left.loc.end.line === right.loc.start.line; + } + } +}); + +// @typescript-eslint/utils/dist/ts-estree.js +var require_ts_estree = __commonJS({ + "@typescript-eslint/utils/dist/ts-estree.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.TSESTree = exports.AST_TOKEN_TYPES = exports.AST_NODE_TYPES = void 0; + var types_1 = __require("@typescript-eslint/types"); + Object.defineProperty(exports, "AST_NODE_TYPES", { enumerable: true, get: function() { + return types_1.AST_NODE_TYPES; + } }); + Object.defineProperty(exports, "AST_TOKEN_TYPES", { enumerable: true, get: function() { + return types_1.AST_TOKEN_TYPES; + } }); + Object.defineProperty(exports, "TSESTree", { enumerable: true, get: function() { + return types_1.TSESTree; + } }); + } +}); + +// @typescript-eslint/utils/dist/ast-utils/predicates.js +var require_predicates2 = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/predicates.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isLoop = exports.isImportKeyword = exports.isTypeKeyword = exports.isAwaitKeyword = exports.isAwaitExpression = exports.isIdentifier = exports.isConstructor = exports.isClassOrTypeElement = exports.isTSConstructorType = exports.isTSFunctionType = exports.isFunctionOrFunctionType = exports.isFunctionType = exports.isFunction = exports.isVariableDeclarator = exports.isTypeAssertion = exports.isLogicalOrOperator = exports.isOptionalCallExpression = exports.isNotNonNullAssertionPunctuator = exports.isNonNullAssertionPunctuator = exports.isNotOptionalChainPunctuator = exports.isOptionalChainPunctuator = void 0; + exports.isSetter = isSetter; + var ts_estree_1 = require_ts_estree(); + var helpers_1 = require_helpers(); + exports.isOptionalChainPunctuator = (0, helpers_1.isTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Punctuator, { value: "?." }); + exports.isNotOptionalChainPunctuator = (0, helpers_1.isNotTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Punctuator, { value: "?." }); + exports.isNonNullAssertionPunctuator = (0, helpers_1.isTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Punctuator, { value: "!" }); + exports.isNotNonNullAssertionPunctuator = (0, helpers_1.isNotTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Punctuator, { value: "!" }); + exports.isOptionalCallExpression = (0, helpers_1.isNodeOfTypeWithConditions)( + ts_estree_1.AST_NODE_TYPES.CallExpression, + // this flag means the call expression itself is option + // i.e. it is foo.bar?.() and not foo?.bar() + { optional: true } + ); + exports.isLogicalOrOperator = (0, helpers_1.isNodeOfTypeWithConditions)(ts_estree_1.AST_NODE_TYPES.LogicalExpression, { operator: "||" }); + exports.isTypeAssertion = (0, helpers_1.isNodeOfTypes)([ + ts_estree_1.AST_NODE_TYPES.TSAsExpression, + ts_estree_1.AST_NODE_TYPES.TSTypeAssertion + ]); + exports.isVariableDeclarator = (0, helpers_1.isNodeOfType)(ts_estree_1.AST_NODE_TYPES.VariableDeclarator); + var functionTypes = [ + ts_estree_1.AST_NODE_TYPES.ArrowFunctionExpression, + ts_estree_1.AST_NODE_TYPES.FunctionDeclaration, + ts_estree_1.AST_NODE_TYPES.FunctionExpression + ]; + exports.isFunction = (0, helpers_1.isNodeOfTypes)(functionTypes); + var functionTypeTypes = [ + ts_estree_1.AST_NODE_TYPES.TSCallSignatureDeclaration, + ts_estree_1.AST_NODE_TYPES.TSConstructorType, + ts_estree_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, + ts_estree_1.AST_NODE_TYPES.TSDeclareFunction, + ts_estree_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, + ts_estree_1.AST_NODE_TYPES.TSFunctionType, + ts_estree_1.AST_NODE_TYPES.TSMethodSignature + ]; + exports.isFunctionType = (0, helpers_1.isNodeOfTypes)(functionTypeTypes); + exports.isFunctionOrFunctionType = (0, helpers_1.isNodeOfTypes)([ + ...functionTypes, + ...functionTypeTypes + ]); + exports.isTSFunctionType = (0, helpers_1.isNodeOfType)(ts_estree_1.AST_NODE_TYPES.TSFunctionType); + exports.isTSConstructorType = (0, helpers_1.isNodeOfType)(ts_estree_1.AST_NODE_TYPES.TSConstructorType); + exports.isClassOrTypeElement = (0, helpers_1.isNodeOfTypes)([ + // ClassElement + ts_estree_1.AST_NODE_TYPES.PropertyDefinition, + ts_estree_1.AST_NODE_TYPES.FunctionExpression, + ts_estree_1.AST_NODE_TYPES.MethodDefinition, + ts_estree_1.AST_NODE_TYPES.TSAbstractPropertyDefinition, + ts_estree_1.AST_NODE_TYPES.TSAbstractMethodDefinition, + ts_estree_1.AST_NODE_TYPES.TSEmptyBodyFunctionExpression, + ts_estree_1.AST_NODE_TYPES.TSIndexSignature, + // TypeElement + ts_estree_1.AST_NODE_TYPES.TSCallSignatureDeclaration, + ts_estree_1.AST_NODE_TYPES.TSConstructSignatureDeclaration, + // AST_NODE_TYPES.TSIndexSignature, + ts_estree_1.AST_NODE_TYPES.TSMethodSignature, + ts_estree_1.AST_NODE_TYPES.TSPropertySignature + ]); + exports.isConstructor = (0, helpers_1.isNodeOfTypeWithConditions)(ts_estree_1.AST_NODE_TYPES.MethodDefinition, { kind: "constructor" }); + function isSetter(node) { + return !!node && (node.type === ts_estree_1.AST_NODE_TYPES.MethodDefinition || node.type === ts_estree_1.AST_NODE_TYPES.Property) && node.kind === "set"; + } + exports.isIdentifier = (0, helpers_1.isNodeOfType)(ts_estree_1.AST_NODE_TYPES.Identifier); + exports.isAwaitExpression = (0, helpers_1.isNodeOfType)(ts_estree_1.AST_NODE_TYPES.AwaitExpression); + exports.isAwaitKeyword = (0, helpers_1.isTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Identifier, { value: "await" }); + exports.isTypeKeyword = (0, helpers_1.isTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Identifier, { value: "type" }); + exports.isImportKeyword = (0, helpers_1.isTokenOfTypeWithConditions)(ts_estree_1.AST_TOKEN_TYPES.Keyword, { value: "import" }); + exports.isLoop = (0, helpers_1.isNodeOfTypes)([ + ts_estree_1.AST_NODE_TYPES.DoWhileStatement, + ts_estree_1.AST_NODE_TYPES.ForStatement, + ts_estree_1.AST_NODE_TYPES.ForInStatement, + ts_estree_1.AST_NODE_TYPES.ForOfStatement, + ts_estree_1.AST_NODE_TYPES.WhileStatement + ]); + } +}); + +// @typescript-eslint/utils/dist/ast-utils/index.js +var require_ast_utils = __commonJS({ + "@typescript-eslint/utils/dist/ast-utils/index.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __exportStar = exports && exports.__exportStar || function(m, exports2) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + __exportStar(require_eslint_utils(), exports); + __exportStar(require_helpers(), exports); + __exportStar(require_misc(), exports); + __exportStar(require_predicates2(), exports); + } +}); + +// @typescript-eslint/utils/dist/eslint-utils/deepMerge.js +var require_deepMerge = __commonJS({ + "@typescript-eslint/utils/dist/eslint-utils/deepMerge.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isObjectNotArray = isObjectNotArray; + exports.deepMerge = deepMerge; + function isObjectNotArray(obj) { + return typeof obj === "object" && obj != null && !Array.isArray(obj); + } + function deepMerge(first = {}, second = {}) { + const keys = /* @__PURE__ */ new Set([...Object.keys(first), ...Object.keys(second)]); + return Object.fromEntries([...keys].map((key) => { + const firstHasKey = key in first; + const secondHasKey = key in second; + const firstValue = first[key]; + const secondValue = second[key]; + let value; + if (firstHasKey && secondHasKey) { + if (isObjectNotArray(firstValue) && isObjectNotArray(secondValue)) { + value = deepMerge(firstValue, secondValue); + } else { + value = secondValue; + } + } else if (firstHasKey) { + value = firstValue; + } else { + value = secondValue; + } + return [key, value]; + })); + } + } +}); + +// @typescript-eslint/utils/dist/eslint-utils/applyDefault.js +var require_applyDefault = __commonJS({ + "@typescript-eslint/utils/dist/eslint-utils/applyDefault.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.applyDefault = applyDefault; + var deepMerge_1 = require_deepMerge(); + function applyDefault(defaultOptions, userOptions) { + const options = structuredClone(defaultOptions); + if (userOptions == null) { + return options; + } + options.forEach((opt, i) => { + if (userOptions[i] !== void 0) { + const userOpt = userOptions[i]; + if ((0, deepMerge_1.isObjectNotArray)(userOpt) && (0, deepMerge_1.isObjectNotArray)(opt)) { + options[i] = (0, deepMerge_1.deepMerge)(opt, userOpt); + } else { + options[i] = userOpt; + } + } + }); + return options; + } + } +}); + +// @typescript-eslint/utils/dist/eslint-utils/RuleCreator.js +var require_RuleCreator = __commonJS({ + "@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RuleCreator = RuleCreator2; + var applyDefault_1 = require_applyDefault(); + function RuleCreator2(urlCreator) { + return function createNamedRule({ meta, name, ...rule }) { + const ruleWithDocs = createRule({ + meta: { + ...meta, + docs: { + ...meta.docs, + url: urlCreator(name) + } + }, + name, + ...rule + }); + return ruleWithDocs; + }; + } + function createRule({ + create, + // Keep accepting deprecated defaultOptions for backward compatibility. + // eslint-disable-next-line @typescript-eslint/no-deprecated + defaultOptions, + meta, + name + }) { + const resolvedDefaultOptions = meta.defaultOptions ?? defaultOptions ?? []; + return { + create(context) { + const optionsWithDefault = (0, applyDefault_1.applyDefault)(resolvedDefaultOptions, context.options); + return create(context, optionsWithDefault); + }, + defaultOptions, + meta, + name + }; + } + RuleCreator2.withoutDocs = function withoutDocs(args) { + return createRule(args); + }; + } +}); + +// syntax-rule-utilities-entry.mjs +var ASTUtils = __toESM(require_ast_utils(), 1); +var import_RuleCreator = __toESM(require_RuleCreator(), 1); +import { findVariable, isParenthesized, isOpeningParenToken, isClosingParenToken } from "@eslint-community/eslint-utils"; +import { AST_NODE_TYPES, AST_TOKEN_TYPES } from "@typescript-eslint/types"; +import * as Scope from "@typescript-eslint/scope-manager"; +var ESLintUtils = { RuleCreator: import_RuleCreator.RuleCreator }; +var TSESLint = { Scope }; +var export_RuleCreator = import_RuleCreator.RuleCreator; +export { + ASTUtils, + AST_NODE_TYPES, + AST_TOKEN_TYPES, + ESLintUtils, + export_RuleCreator as RuleCreator, + TSESLint, + findVariable, + isClosingParenToken, + isOpeningParenToken, + isParenthesized +}; diff --git a/config/oxlint/vendor/testing-library.mjs b/config/oxlint/vendor/testing-library.mjs new file mode 100644 index 000000000000..a959be37df7c --- /dev/null +++ b/config/oxlint/vendor/testing-library.mjs @@ -0,0 +1,2466 @@ +/** Generated by build.mjs. See README.md for source versions, licenses and supported naming selectors. */ +import {createRequire as createVendorRequire} from 'node:module'; +const require = createVendorRequire(import.meta.url); +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, { + get: (a, b) => (typeof require !== "undefined" ? require : a)[b] +}) : x)(function(x) { + if (typeof require !== "undefined") return require.apply(this, arguments); + throw Error('Dynamic require of "' + x + '" is not supported'); +}); +var __commonJS = (cb, mod) => function __require2() { + try { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; + } catch (e) { + throw mod = 0, e; + } +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); + +// eslint-plugin-testing-library/dist/utils/compat.js +var require_compat = __commonJS({ + "eslint-plugin-testing-library/dist/utils/compat.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getDeclaredVariables = exports.getScope = exports.getSourceCode = exports.getFilename = void 0; + var getFilename = (context) => { + var _a; + return (_a = context.filename) !== null && _a !== void 0 ? _a : context.getFilename(); + }; + exports.getFilename = getFilename; + var getSourceCode = (context) => { + var _a; + return (_a = context.sourceCode) !== null && _a !== void 0 ? _a : context.getSourceCode(); + }; + exports.getSourceCode = getSourceCode; + var getScope = (context, node) => { + var _a, _b, _c; + return (_c = (_b = (_a = (0, exports.getSourceCode)(context)).getScope) === null || _b === void 0 ? void 0 : _b.call(_a, node)) !== null && _c !== void 0 ? _c : context.getScope(); + }; + exports.getScope = getScope; + var getDeclaredVariables = (context, node) => { + var _a, _b, _c; + return (_c = (_b = (_a = (0, exports.getSourceCode)(context)).getDeclaredVariables) === null || _b === void 0 ? void 0 : _b.call(_a, node)) !== null && _c !== void 0 ? _c : context.getDeclaredVariables(node); + }; + exports.getDeclaredVariables = getDeclaredVariables; + } +}); + +// eslint-plugin-testing-library/dist/utils/file-import.js +var require_file_import = __commonJS({ + "eslint-plugin-testing-library/dist/utils/file-import.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.importDefault = void 0; + var interopRequireDefault = (obj) => (obj === null || obj === void 0 ? void 0 : obj.__esModule) ? obj : { default: obj }; + var importDefault = (moduleName) => interopRequireDefault(__require(moduleName)).default; + exports.importDefault = importDefault; + } +}); + +// eslint-plugin-testing-library/dist/utils/types.js +var require_types = __commonJS({ + "eslint-plugin-testing-library/dist/utils/types.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.SUPPORTED_TESTING_FRAMEWORKS = void 0; + exports.SUPPORTED_TESTING_FRAMEWORKS = [ + "dom", + "angular", + "react", + "vue", + "svelte", + "marko" + ]; + } +}); + +// eslint-plugin-testing-library/dist/node-utils/is-node-of-type.js +var require_is_node_of_type = __commonJS({ + "eslint-plugin-testing-library/dist/node-utils/is-node-of-type.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isFunctionDeclaration = exports.isFunctionExpression = exports.isReturnStatement = exports.isProperty = exports.isObjectPattern = exports.isObjectExpression = exports.isNewExpression = exports.isMemberExpression = exports.isTemplateLiteral = exports.isLiteral = exports.isJSXAttribute = exports.isImportSpecifier = exports.isImportNamespaceSpecifier = exports.isImportExpression = exports.isTSImportEqualsDeclaration = exports.isImportDefaultSpecifier = exports.isImportDeclaration = exports.isSequenceExpression = exports.isAssignmentExpression = exports.isVariableDeclaration = exports.isExpressionStatement = exports.isCallExpression = exports.isBlockStatement = exports.isArrowFunctionExpression = exports.isArrayExpression = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + exports.isArrayExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ArrayExpression); + exports.isArrowFunctionExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ArrowFunctionExpression); + exports.isBlockStatement = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.BlockStatement); + exports.isCallExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.CallExpression); + exports.isExpressionStatement = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ExpressionStatement); + exports.isVariableDeclaration = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.VariableDeclaration); + exports.isAssignmentExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.AssignmentExpression); + exports.isSequenceExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.SequenceExpression); + exports.isImportDeclaration = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ImportDeclaration); + exports.isImportDefaultSpecifier = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ImportDefaultSpecifier); + exports.isTSImportEqualsDeclaration = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.TSImportEqualsDeclaration); + exports.isImportExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ImportExpression); + exports.isImportNamespaceSpecifier = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ImportNamespaceSpecifier); + exports.isImportSpecifier = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ImportSpecifier); + exports.isJSXAttribute = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.JSXAttribute); + exports.isLiteral = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.Literal); + exports.isTemplateLiteral = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.TemplateLiteral); + exports.isMemberExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.MemberExpression); + exports.isNewExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.NewExpression); + exports.isObjectExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ObjectExpression); + exports.isObjectPattern = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ObjectPattern); + exports.isProperty = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.Property); + exports.isReturnStatement = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.ReturnStatement); + exports.isFunctionExpression = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.FunctionExpression); + exports.isFunctionDeclaration = utils_1.ASTUtils.isNodeOfType(utils_1.AST_NODE_TYPES.FunctionDeclaration); + } +}); + +// eslint-plugin-testing-library/dist/node-utils/index.js +var require_node_utils = __commonJS({ + "eslint-plugin-testing-library/dist/node-utils/index.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __exportStar = exports && exports.__exportStar || function(m, exports2) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.findClosestCallExpressionNode = findClosestCallExpressionNode; + exports.findClosestVariableDeclaratorNode = findClosestVariableDeclaratorNode; + exports.findClosestFunctionExpressionNode = findClosestFunctionExpressionNode; + exports.findClosestCallNode = findClosestCallNode; + exports.hasThenProperty = hasThenProperty; + exports.hasPromiseHandlerProperty = hasPromiseHandlerProperty; + exports.hasChainedPromiseHandler = hasChainedPromiseHandler; + exports.isPromiseIdentifier = isPromiseIdentifier; + exports.isPromiseAll = isPromiseAll; + exports.isPromiseAllSettled = isPromiseAllSettled; + exports.isPromisesArrayResolved = isPromisesArrayResolved; + exports.isPromiseHandled = isPromiseHandled; + exports.getVariableReferences = getVariableReferences; + exports.getInnermostFunctionScope = getInnermostFunctionScope; + exports.getFunctionReturnStatementNode = getFunctionReturnStatementNode; + exports.getPropertyIdentifierNode = getPropertyIdentifierNode; + exports.getDeepestIdentifierNode = getDeepestIdentifierNode; + exports.getReferenceNode = getReferenceNode; + exports.getFunctionName = getFunctionName; + exports.getImportModuleName = getImportModuleName; + exports.getAssertNodeInfo = getAssertNodeInfo; + exports.hasClosestExpectHandlesPromise = hasClosestExpectHandlesPromise; + exports.getInnermostReturningFunction = getInnermostReturningFunction; + exports.hasImportMatch = hasImportMatch; + exports.getStatementCallExpression = getStatementCallExpression; + exports.isEmptyFunction = isEmptyFunction; + exports.findImportSpecifier = findImportSpecifier; + var scope_manager_1 = __require("@typescript-eslint/scope-manager"); + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var utils_2 = require_utils(); + var is_node_of_type_1 = require_is_node_of_type(); + __exportStar(require_is_node_of_type(), exports); + var ValidLeftHandSideExpressions = [ + utils_1.AST_NODE_TYPES.CallExpression, + utils_1.AST_NODE_TYPES.ClassExpression, + utils_1.AST_NODE_TYPES.ClassDeclaration, + utils_1.AST_NODE_TYPES.FunctionExpression, + utils_1.AST_NODE_TYPES.Literal, + utils_1.AST_NODE_TYPES.TemplateLiteral, + utils_1.AST_NODE_TYPES.MemberExpression, + utils_1.AST_NODE_TYPES.ArrayExpression, + utils_1.AST_NODE_TYPES.ArrayPattern, + utils_1.AST_NODE_TYPES.ClassExpression, + utils_1.AST_NODE_TYPES.FunctionExpression, + utils_1.AST_NODE_TYPES.Identifier, + utils_1.AST_NODE_TYPES.JSXElement, + utils_1.AST_NODE_TYPES.JSXFragment, + utils_1.AST_NODE_TYPES.JSXOpeningElement, + utils_1.AST_NODE_TYPES.MetaProperty, + utils_1.AST_NODE_TYPES.ObjectExpression, + utils_1.AST_NODE_TYPES.ObjectPattern, + utils_1.AST_NODE_TYPES.Super, + utils_1.AST_NODE_TYPES.ThisExpression, + utils_1.AST_NODE_TYPES.TSNullKeyword, + utils_1.AST_NODE_TYPES.TaggedTemplateExpression, + utils_1.AST_NODE_TYPES.TSNonNullExpression, + utils_1.AST_NODE_TYPES.TSAsExpression, + utils_1.AST_NODE_TYPES.ArrowFunctionExpression + ]; + function findClosestCallExpressionNode(node, shouldRestrictInnerScope = false) { + if ((0, is_node_of_type_1.isCallExpression)(node)) { + return node; + } + if (!(node === null || node === void 0 ? void 0 : node.parent)) { + return null; + } + if (shouldRestrictInnerScope && !ValidLeftHandSideExpressions.includes(node.parent.type)) { + return null; + } + return findClosestCallExpressionNode(node.parent, shouldRestrictInnerScope); + } + function findClosestVariableDeclaratorNode(node) { + if (!node) { + return null; + } + if (utils_1.ASTUtils.isVariableDeclarator(node)) { + return node; + } + return findClosestVariableDeclaratorNode(node.parent); + } + function findClosestFunctionExpressionNode(node) { + if (!node) { + return null; + } + if ((0, is_node_of_type_1.isArrowFunctionExpression)(node) || (0, is_node_of_type_1.isFunctionExpression)(node) || (0, is_node_of_type_1.isFunctionDeclaration)(node)) { + return node; + } + return findClosestFunctionExpressionNode(node.parent); + } + function findClosestCallNode(node, name) { + if (!node.parent) { + return null; + } + if ((0, is_node_of_type_1.isCallExpression)(node) && utils_1.ASTUtils.isIdentifier(node.callee) && node.callee.name === name) { + return node; + } else { + return findClosestCallNode(node.parent, name); + } + } + function hasThenProperty(node) { + return (0, is_node_of_type_1.isMemberExpression)(node) && utils_1.ASTUtils.isIdentifier(node.property) && node.property.name === "then"; + } + function hasPromiseHandlerProperty(node) { + return (0, is_node_of_type_1.isMemberExpression)(node) && utils_1.ASTUtils.isIdentifier(node.property) && ["then", "catch", "finally"].includes(node.property.name); + } + function hasChainedPromiseHandler(node) { + const parent = node.parent; + if ((0, is_node_of_type_1.isCallExpression)(parent) && parent.parent) { + return hasPromiseHandlerProperty(parent.parent); + } + return !!parent && hasPromiseHandlerProperty(parent); + } + function isPromiseIdentifier(node) { + return utils_1.ASTUtils.isIdentifier(node) && node.name === "Promise"; + } + function isPromiseAll(node) { + return (0, is_node_of_type_1.isMemberExpression)(node.callee) && isPromiseIdentifier(node.callee.object) && utils_1.ASTUtils.isIdentifier(node.callee.property) && node.callee.property.name === "all"; + } + function isPromiseAllSettled(node) { + return (0, is_node_of_type_1.isMemberExpression)(node.callee) && isPromiseIdentifier(node.callee.object) && utils_1.ASTUtils.isIdentifier(node.callee.property) && node.callee.property.name === "allSettled"; + } + function isPromisesArrayResolved(node) { + const closestCallExpression = findClosestCallExpressionNode(node, true); + if (!closestCallExpression) { + return false; + } + return !!closestCallExpression.parent && (0, is_node_of_type_1.isArrayExpression)(closestCallExpression.parent) && (0, is_node_of_type_1.isCallExpression)(closestCallExpression.parent.parent) && (isPromiseAll(closestCallExpression.parent.parent) || isPromiseAllSettled(closestCallExpression.parent.parent)); + } + function isPromiseHandled(nodeIdentifier) { + const closestCallExpressionNode = findClosestCallExpressionNode(nodeIdentifier, true); + const callRootExpression = closestCallExpressionNode == null ? null : getRootExpression(closestCallExpressionNode); + const suspiciousNodes = [nodeIdentifier, callRootExpression].filter((node) => node != null); + return suspiciousNodes.some((node) => { + if (!node.parent) + return false; + if (utils_1.ASTUtils.isAwaitExpression(node.parent)) + return true; + if ((0, is_node_of_type_1.isArrowFunctionExpression)(node.parent) || (0, is_node_of_type_1.isReturnStatement)(node.parent)) + return true; + if (hasClosestExpectHandlesPromise(node.parent)) + return true; + if (hasChainedPromiseHandler(node)) + return true; + if (isPromisesArrayResolved(node)) + return true; + }); + } + function getRootExpression(expression) { + const { parent } = expression; + if (parent == null) + return expression; + switch (parent.type) { + case utils_1.AST_NODE_TYPES.ConditionalExpression: + return getRootExpression(parent); + case utils_1.AST_NODE_TYPES.LogicalExpression: { + let rootExpression; + switch (parent.operator) { + case "??": + case "||": + rootExpression = getRootExpression(parent); + break; + case "&&": + rootExpression = parent.right === expression ? getRootExpression(parent) : expression; + break; + } + return rootExpression !== null && rootExpression !== void 0 ? rootExpression : expression; + } + case utils_1.AST_NODE_TYPES.SequenceExpression: + return parent.expressions[parent.expressions.length - 1] === expression ? getRootExpression(parent) : expression; + case utils_1.AST_NODE_TYPES.ChainExpression: + return getRootExpression(parent); + default: + return expression; + } + } + function getVariableReferences(context, node) { + var _a, _b, _c; + if (utils_1.ASTUtils.isVariableDeclarator(node)) { + return (_c = (_b = (_a = (0, utils_2.getDeclaredVariables)(context, node)[0]) === null || _a === void 0 ? void 0 : _a.references) === null || _b === void 0 ? void 0 : _b.slice(1)) !== null && _c !== void 0 ? _c : []; + } + return []; + } + function getInnermostFunctionScope(context, asyncQueryNode) { + const innermostScope = utils_1.ASTUtils.getInnermostScope((0, utils_2.getScope)(context, asyncQueryNode), asyncQueryNode); + if (innermostScope.type === scope_manager_1.ScopeType.function && utils_1.ASTUtils.isFunction(innermostScope.block)) { + return innermostScope; + } + return null; + } + function getFunctionReturnStatementNode(functionNode) { + if ((0, is_node_of_type_1.isBlockStatement)(functionNode.body)) { + const returnStatementNode = functionNode.body.body.find((statement) => (0, is_node_of_type_1.isReturnStatement)(statement)); + if (!returnStatementNode) { + return null; + } + return returnStatementNode.argument; + } else if (functionNode.expression) { + return functionNode.body; + } + return null; + } + function getPropertyIdentifierNode(node) { + if (utils_1.ASTUtils.isIdentifier(node)) { + return node; + } + if ((0, is_node_of_type_1.isMemberExpression)(node)) { + return getPropertyIdentifierNode(node.object); + } + if ((0, is_node_of_type_1.isCallExpression)(node)) { + return getPropertyIdentifierNode(node.callee); + } + if ((0, is_node_of_type_1.isExpressionStatement)(node)) { + return getPropertyIdentifierNode(node.expression); + } + if (utils_1.ASTUtils.isAwaitExpression(node)) { + return getPropertyIdentifierNode(node.argument); + } + return null; + } + function getDeepestIdentifierNode(node) { + if (utils_1.ASTUtils.isIdentifier(node)) { + return node; + } + if ((0, is_node_of_type_1.isMemberExpression)(node) && utils_1.ASTUtils.isIdentifier(node.property)) { + return node.property; + } + if ((0, is_node_of_type_1.isCallExpression)(node)) { + return getDeepestIdentifierNode(node.callee); + } + if (utils_1.ASTUtils.isAwaitExpression(node)) { + return getDeepestIdentifierNode(node.argument); + } + return null; + } + function getReferenceNode(node) { + if (node.parent && ((0, is_node_of_type_1.isMemberExpression)(node.parent) || (0, is_node_of_type_1.isCallExpression)(node.parent))) { + return getReferenceNode(node.parent); + } + return node; + } + function getFunctionName(node) { + var _a, _b; + return (_b = (_a = utils_1.ASTUtils.getFunctionNameWithKind(node).match(/('\w+')/g)) === null || _a === void 0 ? void 0 : _a[0].replace(/'/g, "")) !== null && _b !== void 0 ? _b : ""; + } + function getImportModuleName(node) { + if ((0, is_node_of_type_1.isImportDeclaration)(node) && typeof node.source.value === "string") { + return node.source.value; + } + if ((0, is_node_of_type_1.isCallExpression)(node) && (0, is_node_of_type_1.isLiteral)(node.arguments[0]) && typeof node.arguments[0].value === "string") { + return node.arguments[0].value; + } + return void 0; + } + function getAssertNodeInfo(node) { + const emptyInfo = { matcher: null, isNegated: false }; + if (!(0, is_node_of_type_1.isCallExpression)(node.object) || !utils_1.ASTUtils.isIdentifier(node.object.callee)) { + return emptyInfo; + } + if (node.object.callee.name !== "expect") { + return emptyInfo; + } + let matcher = utils_1.ASTUtils.getPropertyName(node); + const isNegated = matcher === "not"; + if (isNegated) { + matcher = node.parent && (0, is_node_of_type_1.isMemberExpression)(node.parent) ? utils_1.ASTUtils.getPropertyName(node.parent) : null; + } + if (!matcher) { + return emptyInfo; + } + return { matcher, isNegated }; + } + var matcherNamesHandlePromise = [ + "resolves", + "rejects", + "toResolve", + "toReject", + "toBeRejected", + "toBeRejectedWith", + "toBeRejectedWithError", + "toBePending", + "toBeResolved", + "toBeResolvedTo" + ]; + function hasClosestExpectHandlesPromise(node) { + if ((0, is_node_of_type_1.isCallExpression)(node) && utils_1.ASTUtils.isIdentifier(node.callee) && node.parent && (0, is_node_of_type_1.isMemberExpression)(node.parent) && ["expect", "expectAsync"].includes(node.callee.name)) { + const expectMatcher = node.parent.property; + return utils_1.ASTUtils.isIdentifier(expectMatcher) && matcherNamesHandlePromise.includes(expectMatcher.name); + } + if (!node.parent) { + return false; + } + return hasClosestExpectHandlesPromise(node.parent); + } + function getInnermostReturningFunction(context, node) { + const functionScope = getInnermostFunctionScope(context, node); + if (!functionScope) { + return void 0; + } + const returnStatementNode = getFunctionReturnStatementNode(functionScope.block); + if (!returnStatementNode) { + return void 0; + } + const returnStatementIdentifier = getDeepestIdentifierNode(returnStatementNode); + if ((returnStatementIdentifier === null || returnStatementIdentifier === void 0 ? void 0 : returnStatementIdentifier.name) !== node.name) { + return void 0; + } + return functionScope.block; + } + function hasImportMatch(importNode, identifierName) { + if (utils_1.ASTUtils.isIdentifier(importNode)) { + return importNode.name === identifierName; + } + return importNode.local.name === identifierName; + } + function getStatementCallExpression(statement) { + if ((0, is_node_of_type_1.isExpressionStatement)(statement)) { + const { expression } = statement; + if ((0, is_node_of_type_1.isCallExpression)(expression)) { + return expression; + } + if (utils_1.ASTUtils.isAwaitExpression(expression) && (0, is_node_of_type_1.isCallExpression)(expression.argument)) { + return expression.argument; + } + if ((0, is_node_of_type_1.isAssignmentExpression)(expression)) { + if ((0, is_node_of_type_1.isCallExpression)(expression.right)) { + return expression.right; + } + if (utils_1.ASTUtils.isAwaitExpression(expression.right) && (0, is_node_of_type_1.isCallExpression)(expression.right.argument)) { + return expression.right.argument; + } + } + } + if ((0, is_node_of_type_1.isReturnStatement)(statement) && (0, is_node_of_type_1.isCallExpression)(statement.argument)) { + return statement.argument; + } + if ((0, is_node_of_type_1.isVariableDeclaration)(statement)) { + for (const declaration of statement.declarations) { + if ((0, is_node_of_type_1.isCallExpression)(declaration.init)) { + return declaration.init; + } + } + } + return void 0; + } + function isEmptyFunction(node) { + if (utils_1.ASTUtils.isFunction(node) && (0, is_node_of_type_1.isBlockStatement)(node.body)) { + return node.body.body.length === 0; + } + return false; + } + function findImportSpecifier(specifierName, node) { + if ((0, is_node_of_type_1.isImportDeclaration)(node)) { + const namedExport = node.specifiers.find((n) => { + return (0, is_node_of_type_1.isImportSpecifier)(n) && utils_1.ASTUtils.isIdentifier(n.imported) && [n.imported.name, n.local.name].includes(specifierName); + }); + if (namedExport) { + return namedExport; + } + return node.specifiers.find((n) => (0, is_node_of_type_1.isImportNamespaceSpecifier)(n)); + } else { + if (!utils_1.ASTUtils.isVariableDeclarator(node.parent)) { + return void 0; + } + const requireNode = node.parent; + if (utils_1.ASTUtils.isIdentifier(requireNode.id)) { + return requireNode.id; + } + if (!(0, is_node_of_type_1.isObjectPattern)(requireNode.id)) { + return void 0; + } + const property = requireNode.id.properties.find((n) => (0, is_node_of_type_1.isProperty)(n) && utils_1.ASTUtils.isIdentifier(n.key) && n.key.name === specifierName); + if (!property) { + return void 0; + } + return property.key; + } + } + } +}); + +// eslint-plugin-testing-library/dist/utils/is-testing-library-module.js +var require_is_testing_library_module = __commonJS({ + "eslint-plugin-testing-library/dist/utils/is-testing-library-module.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.isTestingLibraryModule = exports.isCustomTestingLibraryModule = exports.isOfficialTestingLibraryModule = void 0; + var _1 = require_utils(); + var isOfficialTestingLibraryModule = (importSourceName) => [..._1.OLD_LIBRARY_MODULES, ..._1.LIBRARY_MODULES, _1.USER_EVENT_MODULE].includes(importSourceName); + exports.isOfficialTestingLibraryModule = isOfficialTestingLibraryModule; + var isCustomTestingLibraryModule = (importSourceName, customModuleSetting) => typeof customModuleSetting === "string" && importSourceName.endsWith(customModuleSetting); + exports.isCustomTestingLibraryModule = isCustomTestingLibraryModule; + var isTestingLibraryModule = (importSourceName, customModuleSetting) => (0, exports.isOfficialTestingLibraryModule)(importSourceName) || (0, exports.isCustomTestingLibraryModule)(importSourceName, customModuleSetting); + exports.isTestingLibraryModule = isTestingLibraryModule; + } +}); + +// eslint-plugin-testing-library/dist/node-utils/accessors.js +var require_accessors = __commonJS({ + "eslint-plugin-testing-library/dist/node-utils/accessors.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.getAccessorValue = exports.isSupportedAccessor = exports.isIdentifier = exports.getStringValue = exports.isStringNode = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var is_node_of_type_1 = require_is_node_of_type(); + var isStringLiteral = (node, value) => (0, is_node_of_type_1.isLiteral)(node) && typeof node.value === "string" && (value === void 0 || node.value === value); + var isSimpleTemplateLiteral = (node, value) => (0, is_node_of_type_1.isTemplateLiteral)(node) && node.quasis.length === 1 && (value === void 0 || node.quasis[0].value.raw === value); + var isStringNode = (node, specifics) => isStringLiteral(node, specifics) || isSimpleTemplateLiteral(node, specifics); + exports.isStringNode = isStringNode; + var getStringValue = (node) => isSimpleTemplateLiteral(node) ? node.quasis[0].value.raw : node.value; + exports.getStringValue = getStringValue; + var isIdentifier = (node, name) => utils_1.ASTUtils.isIdentifier(node) && (name === void 0 || node.name === name); + exports.isIdentifier = isIdentifier; + var isSupportedAccessor = (node, value) => (0, exports.isIdentifier)(node, value) || (0, exports.isStringNode)(node, value); + exports.isSupportedAccessor = isSupportedAccessor; + var getAccessorValue = (accessor) => accessor.type === utils_1.AST_NODE_TYPES.Identifier ? accessor.name : (0, exports.getStringValue)(accessor); + exports.getAccessorValue = getAccessorValue; + } +}); + +// eslint-plugin-testing-library/dist/utils/resolve-to-testing-library-fn.js +var require_resolve_to_testing_library_fn = __commonJS({ + "eslint-plugin-testing-library/dist/utils/resolve-to-testing-library-fn.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.resolveToTestingLibraryFn = exports.getNodeChain = void 0; + var scope_manager_1 = __require("@typescript-eslint/scope-manager"); + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var node_utils_1 = require_node_utils(); + var is_testing_library_module_1 = require_is_testing_library_module(); + var accessors_1 = require_accessors(); + var describeImportDefAsImport = (def) => { + if ((0, node_utils_1.isTSImportEqualsDeclaration)(def.parent)) { + return null; + } + if ((0, node_utils_1.isImportDefaultSpecifier)(def.node)) { + return { + source: def.parent.source.value, + imported: null, + local: def.node.local.name + }; + } + if (!(0, node_utils_1.isImportSpecifier)(def.node)) { + return null; + } + if (def.parent.importKind === "type") { + return null; + } + return { + source: def.parent.source.value, + imported: "name" in def.node.imported ? def.node.imported.name : def.node.imported.value, + local: def.node.local.name + }; + }; + var describeVariableDefAsImport = (def) => { + if (!def.node.init) + return null; + const sourceNode = (0, node_utils_1.isCallExpression)(def.node.init) && (0, accessors_1.isIdentifier)(def.node.init.callee, "require") ? def.node.init.arguments[0] : utils_1.ASTUtils.isAwaitExpression(def.node.init) && (0, node_utils_1.isImportExpression)(def.node.init.argument) ? def.node.init.argument.source : null; + if (!sourceNode || !(0, accessors_1.isStringNode)(sourceNode)) + return null; + if (!(0, node_utils_1.isProperty)(def.name.parent)) + return null; + if (!(0, accessors_1.isSupportedAccessor)(def.name.parent.key)) + return null; + return { + source: (0, accessors_1.getStringValue)(sourceNode), + imported: (0, accessors_1.getAccessorValue)(def.name.parent.key), + local: def.name.name + }; + }; + var describePossibleImportDef = (def) => { + if (def.type === scope_manager_1.DefinitionType.Variable) { + return describeVariableDefAsImport(def); + } + if (def.type === scope_manager_1.DefinitionType.ImportBinding) { + return describeImportDefAsImport(def); + } + return null; + }; + var resolveScope = (scope, identifier) => { + let currentScope = scope; + while (currentScope !== null) { + const ref = currentScope.set.get(identifier); + if (ref && ref.defs.length > 0) { + const def = ref.defs[ref.defs.length - 1]; + const importDetails = describePossibleImportDef(def); + if ((importDetails === null || importDetails === void 0 ? void 0 : importDetails.local) === identifier) { + return importDetails; + } + return "local"; + } + currentScope = currentScope.upper; + } + return null; + }; + var joinChains = (a, b) => a && b ? [...a, ...b] : null; + var getNodeChain = (node) => { + if ((0, accessors_1.isSupportedAccessor)(node)) { + return [node]; + } + switch (node.type) { + case utils_1.AST_NODE_TYPES.MemberExpression: + return joinChains((0, exports.getNodeChain)(node.object), (0, exports.getNodeChain)(node.property)); + case utils_1.AST_NODE_TYPES.CallExpression: + return (0, exports.getNodeChain)(node.callee); + } + return null; + }; + exports.getNodeChain = getNodeChain; + var resolveToTestingLibraryFn = (node, context) => { + const chain = (0, exports.getNodeChain)(node); + if (!(chain === null || chain === void 0 ? void 0 : chain.length)) + return null; + const identifier = chain[0]; + const scope = context.sourceCode.getScope(identifier); + const maybeImport = resolveScope(scope, (0, accessors_1.getAccessorValue)(identifier)); + if (maybeImport === "local" || maybeImport === null) { + return null; + } + const customModuleSetting = context.settings["testing-library/utils-module"]; + if ((0, is_testing_library_module_1.isTestingLibraryModule)(maybeImport.source, customModuleSetting)) { + return { + original: maybeImport.imported, + local: maybeImport.local + }; + } + return null; + }; + exports.resolveToTestingLibraryFn = resolveToTestingLibraryFn; + } +}); + +// eslint-plugin-testing-library/dist/utils/index.js +var require_utils = __commonJS({ + "eslint-plugin-testing-library/dist/utils/index.js"(exports) { + "use strict"; + var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { + return m[k]; + } }; + } + Object.defineProperty(o, k2, desc); + }) : (function(o, m, k, k2) { + if (k2 === void 0) k2 = k; + o[k2] = m[k]; + })); + var __exportStar = exports && exports.__exportStar || function(m, exports2) { + for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p); + }; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.OLD_LIBRARY_MODULES = exports.USER_EVENT_MODULE = exports.EVENT_HANDLER_METHODS = exports.ABSENCE_MATCHERS = exports.PRESENCE_MATCHERS = exports.ALL_RETURNING_NODES = exports.METHODS_RETURNING_NODES = exports.PROPERTIES_RETURNING_NODES = exports.LIBRARY_MODULES = exports.TESTING_FRAMEWORK_SETUP_HOOKS = exports.EVENTS_SIMULATORS = exports.DEBUG_UTILS = exports.ASYNC_UTILS = exports.ALL_QUERIES_COMBINATIONS = exports.ASYNC_QUERIES_COMBINATIONS = exports.SYNC_QUERIES_COMBINATIONS = exports.ALL_QUERIES_METHODS = exports.ALL_QUERIES_VARIANTS = exports.ASYNC_QUERIES_VARIANTS = exports.SYNC_QUERIES_VARIANTS = exports.getDocsUrl = exports.combineQueries = void 0; + __exportStar(require_compat(), exports); + __exportStar(require_file_import(), exports); + __exportStar(require_types(), exports); + __exportStar(require_resolve_to_testing_library_fn(), exports); + var combineQueries = (variants, methods) => { + const combinedQueries = []; + variants.forEach((variant) => { + const variantPrefix = variant.replace("By", ""); + methods.forEach((method) => { + combinedQueries.push(`${variantPrefix}${method}`); + }); + }); + return combinedQueries; + }; + exports.combineQueries = combineQueries; + var getDocsUrl = (ruleName) => `https://github.com/testing-library/eslint-plugin-testing-library/tree/main/docs/rules/${ruleName}.md`; + exports.getDocsUrl = getDocsUrl; + var LIBRARY_MODULES = [ + "@testing-library/dom", + "@testing-library/angular", + "@testing-library/react", + "@testing-library/preact", + "@testing-library/vue", + "@testing-library/svelte", + "@marko/testing-library" + ]; + exports.LIBRARY_MODULES = LIBRARY_MODULES; + var USER_EVENT_MODULE = "@testing-library/user-event"; + exports.USER_EVENT_MODULE = USER_EVENT_MODULE; + var OLD_LIBRARY_MODULES = [ + "dom-testing-library", + "vue-testing-library", + "react-testing-library" + ]; + exports.OLD_LIBRARY_MODULES = OLD_LIBRARY_MODULES; + var SYNC_QUERIES_VARIANTS = [ + "getBy", + "getAllBy", + "queryBy", + "queryAllBy" + ]; + exports.SYNC_QUERIES_VARIANTS = SYNC_QUERIES_VARIANTS; + var ASYNC_QUERIES_VARIANTS = ["findBy", "findAllBy"]; + exports.ASYNC_QUERIES_VARIANTS = ASYNC_QUERIES_VARIANTS; + var ALL_QUERIES_VARIANTS = [ + ...SYNC_QUERIES_VARIANTS, + ...ASYNC_QUERIES_VARIANTS + ]; + exports.ALL_QUERIES_VARIANTS = ALL_QUERIES_VARIANTS; + var ALL_QUERIES_METHODS = [ + "ByLabelText", + "ByPlaceholderText", + "ByText", + "ByAltText", + "ByTitle", + "ByDisplayValue", + "ByRole", + "ByTestId" + ]; + exports.ALL_QUERIES_METHODS = ALL_QUERIES_METHODS; + var SYNC_QUERIES_COMBINATIONS = combineQueries(SYNC_QUERIES_VARIANTS, ALL_QUERIES_METHODS); + exports.SYNC_QUERIES_COMBINATIONS = SYNC_QUERIES_COMBINATIONS; + var ASYNC_QUERIES_COMBINATIONS = combineQueries(ASYNC_QUERIES_VARIANTS, ALL_QUERIES_METHODS); + exports.ASYNC_QUERIES_COMBINATIONS = ASYNC_QUERIES_COMBINATIONS; + var ALL_QUERIES_COMBINATIONS = [ + ...SYNC_QUERIES_COMBINATIONS, + ...ASYNC_QUERIES_COMBINATIONS + ]; + exports.ALL_QUERIES_COMBINATIONS = ALL_QUERIES_COMBINATIONS; + var ASYNC_UTILS = ["waitFor", "waitForElementToBeRemoved"]; + exports.ASYNC_UTILS = ASYNC_UTILS; + var DEBUG_UTILS = [ + "debug", + "logTestingPlaygroundURL", + "prettyDOM", + "logRoles", + "logDOM", + "prettyFormat" + ]; + exports.DEBUG_UTILS = DEBUG_UTILS; + var EVENTS_SIMULATORS = ["fireEvent", "userEvent"]; + exports.EVENTS_SIMULATORS = EVENTS_SIMULATORS; + var TESTING_FRAMEWORK_SETUP_HOOKS = ["beforeEach", "beforeAll"]; + exports.TESTING_FRAMEWORK_SETUP_HOOKS = TESTING_FRAMEWORK_SETUP_HOOKS; + var PROPERTIES_RETURNING_NODES = [ + "activeElement", + "children", + "childElementCount", + "firstChild", + "firstElementChild", + "fullscreenElement", + "lastChild", + "lastElementChild", + "nextElementSibling", + "nextSibling", + "parentElement", + "parentNode", + "pointerLockElement", + "previousElementSibling", + "previousSibling", + "rootNode", + "scripts" + ]; + exports.PROPERTIES_RETURNING_NODES = PROPERTIES_RETURNING_NODES; + var METHODS_RETURNING_NODES = [ + "closest", + "getElementById", + "getElementsByClassName", + "getElementsByName", + "getElementsByTagName", + "getElementsByTagNameNS", + "querySelector", + "querySelectorAll" + ]; + exports.METHODS_RETURNING_NODES = METHODS_RETURNING_NODES; + var EVENT_HANDLER_METHODS = ["click", "select", "submit"]; + exports.EVENT_HANDLER_METHODS = EVENT_HANDLER_METHODS; + var ALL_RETURNING_NODES = [ + ...PROPERTIES_RETURNING_NODES, + ...METHODS_RETURNING_NODES + ]; + exports.ALL_RETURNING_NODES = ALL_RETURNING_NODES; + var PRESENCE_MATCHERS = [ + "toBeOnTheScreen", + "toBeInTheDocument", + "toBeTruthy", + "toBeDefined" + ]; + exports.PRESENCE_MATCHERS = PRESENCE_MATCHERS; + var ABSENCE_MATCHERS = ["toBeNull", "toBeFalsy"]; + exports.ABSENCE_MATCHERS = ABSENCE_MATCHERS; + } +}); + +// eslint-plugin-testing-library/dist/create-testing-library-rule/detect-testing-library-utils.js +var require_detect_testing_library_utils = __commonJS({ + "eslint-plugin-testing-library/dist/create-testing-library-rule/detect-testing-library-utils.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.detectTestingLibraryUtils = detectTestingLibraryUtils; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var node_utils_1 = require_node_utils(); + var utils_2 = require_utils(); + var is_testing_library_module_1 = require_is_testing_library_module(); + var SETTING_OPTION_OFF = "off"; + var REACT_DOM_TEST_UTILS_PACKAGE = "react-dom/test-utils"; + var FIRE_EVENT_NAME = "fireEvent"; + var CREATE_EVENT_NAME = "createEvent"; + var USER_EVENT_NAME = "userEvent"; + var RENDER_NAME = "render"; + function detectTestingLibraryUtils(ruleCreate, { skipRuleReportingCheck = false } = {}) { + return (context, optionsWithDefault) => { + const importedTestingLibraryNodes = []; + let importedCustomModuleNode = null; + let importedUserEventLibraryNode = null; + let importedReactDomTestUtilsNode = null; + const customModuleSetting = context.settings["testing-library/utils-module"]; + const customRendersSetting = context.settings["testing-library/custom-renders"]; + const customQueriesSetting = context.settings["testing-library/custom-queries"]; + function isPotentialTestingLibraryFunction(node, isPotentialFunctionCallback) { + if (!node) { + return false; + } + const referenceNode = (0, node_utils_1.getReferenceNode)(node); + const referenceNodeIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); + if (!referenceNodeIdentifier) { + return false; + } + const importedUtilSpecifier = getTestingLibraryImportedUtilSpecifier(referenceNodeIdentifier); + const originalNodeName = (0, node_utils_1.isImportSpecifier)(importedUtilSpecifier) && utils_1.ASTUtils.isIdentifier(importedUtilSpecifier.imported) && importedUtilSpecifier.local.name !== importedUtilSpecifier.imported.name ? importedUtilSpecifier.imported.name : void 0; + if (!isPotentialFunctionCallback(node.name, originalNodeName)) { + return false; + } + if (isAggressiveModuleReportingEnabled()) { + return true; + } + return isNodeComingFromTestingLibrary(referenceNodeIdentifier); + } + const isAggressiveModuleReportingEnabled = () => !customModuleSetting; + const isAggressiveRenderReportingEnabled = () => { + const isSwitchedOff = customRendersSetting === SETTING_OPTION_OFF; + const hasCustomOptions = Array.isArray(customRendersSetting) && customRendersSetting.length > 0; + return !isSwitchedOff && !hasCustomOptions; + }; + const isAggressiveQueryReportingEnabled = () => { + const isSwitchedOff = customQueriesSetting === SETTING_OPTION_OFF; + const hasCustomOptions = Array.isArray(customQueriesSetting) && customQueriesSetting.length > 0; + return !isSwitchedOff && !hasCustomOptions; + }; + const getCustomModule = () => { + if (!isAggressiveModuleReportingEnabled() && customModuleSetting !== SETTING_OPTION_OFF) { + return customModuleSetting; + } + return void 0; + }; + const getCustomRenders = () => { + if (!isAggressiveRenderReportingEnabled() && customRendersSetting !== SETTING_OPTION_OFF) { + return customRendersSetting; + } + return []; + }; + const getCustomQueries = () => { + if (!isAggressiveQueryReportingEnabled() && customQueriesSetting !== SETTING_OPTION_OFF) { + return customQueriesSetting; + } + return []; + }; + const getTestingLibraryImportNode = () => { + return importedTestingLibraryNodes[0]; + }; + const getAllTestingLibraryImportNodes = () => { + return importedTestingLibraryNodes; + }; + const getCustomModuleImportNode = () => { + return importedCustomModuleNode; + }; + const getTestingLibraryImportName = () => { + return (0, node_utils_1.getImportModuleName)(importedTestingLibraryNodes[0]); + }; + const getCustomModuleImportName = () => { + return (0, node_utils_1.getImportModuleName)(importedCustomModuleNode); + }; + const isTestingLibraryImported = (isStrict = false) => { + const isSomeModuleImported = importedTestingLibraryNodes.length !== 0 || !!importedCustomModuleNode; + return !isStrict && isAggressiveModuleReportingEnabled() || isSomeModuleImported; + }; + const isQuery = (node) => { + const hasQueryPattern = /^(get|query|find)(All)?By.+$/.test(node.name); + if (!hasQueryPattern) { + return false; + } + if (isAggressiveQueryReportingEnabled()) { + return true; + } + const customQueries = getCustomQueries(); + const isBuiltInQuery2 = utils_2.ALL_QUERIES_COMBINATIONS.includes(node.name); + const isReportableCustomQuery = customQueries.some((pattern) => new RegExp(pattern).test(node.name)); + return isBuiltInQuery2 || isReportableCustomQuery; + }; + const isGetQueryVariant = (node) => { + return isQuery(node) && node.name.startsWith("get"); + }; + const isQueryQueryVariant = (node) => { + return isQuery(node) && node.name.startsWith("query"); + }; + const isFindQueryVariant = (node) => { + return isQuery(node) && node.name.startsWith("find"); + }; + const isSyncQuery = (node) => { + return isGetQueryVariant(node) || isQueryQueryVariant(node); + }; + const isAsyncQuery = (node) => { + return isFindQueryVariant(node); + }; + const isCustomQuery = (node) => { + return isQuery(node) && !utils_2.ALL_QUERIES_COMBINATIONS.includes(node.name); + }; + const isBuiltInQuery = (node) => { + return isQuery(node) && utils_2.ALL_QUERIES_COMBINATIONS.includes(node.name); + }; + const isAsyncUtil = (node, validNames = utils_2.ASYNC_UTILS) => { + return isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { + return validNames.includes(identifierNodeName) || !!originalNodeName && validNames.includes(originalNodeName); + }); + }; + const isFireEventUtil = (node) => { + return isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { + return [identifierNodeName, originalNodeName].includes("fireEvent"); + }); + }; + const isUserEventUtil = (node) => { + const userEvent = findImportedUserEventSpecifier(); + let userEventName; + if (userEvent) { + userEventName = userEvent.name; + } else if (isAggressiveModuleReportingEnabled()) { + userEventName = USER_EVENT_NAME; + } + if (!userEventName) { + return false; + } + return node.name === userEventName; + }; + const isFireEventMethod = (node) => { + const fireEventUtil = findImportedTestingLibraryUtilSpecifier(FIRE_EVENT_NAME); + let fireEventUtilName; + if (fireEventUtil) { + fireEventUtilName = utils_1.ASTUtils.isIdentifier(fireEventUtil) ? fireEventUtil.name : fireEventUtil.local.name; + } else if (isAggressiveModuleReportingEnabled()) { + fireEventUtilName = FIRE_EVENT_NAME; + } + if (!fireEventUtilName) { + return false; + } + const parentMemberExpression = node.parent && (0, node_utils_1.isMemberExpression)(node.parent) ? node.parent : void 0; + const parentCallExpression = node.parent && (0, node_utils_1.isCallExpression)(node.parent) ? node.parent : void 0; + if (!parentMemberExpression && !parentCallExpression) { + return false; + } + if (parentCallExpression) { + return [fireEventUtilName, FIRE_EVENT_NAME].includes(node.name); + } + const definedParentMemberExpression = parentMemberExpression; + const regularCall = utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object) && (0, node_utils_1.isCallExpression)(definedParentMemberExpression.parent) && definedParentMemberExpression.object.name === fireEventUtilName && node.name !== FIRE_EVENT_NAME && node.name !== fireEventUtilName; + const wildcardCall = (0, node_utils_1.isMemberExpression)(definedParentMemberExpression.object) && utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object.object) && definedParentMemberExpression.object.object.name === fireEventUtilName && utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object.property) && definedParentMemberExpression.object.property.name === FIRE_EVENT_NAME && node.name !== FIRE_EVENT_NAME && node.name !== fireEventUtilName; + const wildcardCallWithCallExpression = utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object) && definedParentMemberExpression.object.name === fireEventUtilName && utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.property) && definedParentMemberExpression.property.name === FIRE_EVENT_NAME && !(0, node_utils_1.isMemberExpression)(definedParentMemberExpression.parent) && node.name === FIRE_EVENT_NAME && node.name !== fireEventUtilName; + return regularCall || wildcardCall || wildcardCallWithCallExpression; + }; + const isUserEventMethod = (node, userEventSetupVars) => { + const userEvent = findImportedUserEventSpecifier(); + let userEventName; + if (userEvent) { + userEventName = userEvent.name; + } else if (isAggressiveModuleReportingEnabled()) { + userEventName = USER_EVENT_NAME; + } + const parentMemberExpression = node.parent && (0, node_utils_1.isMemberExpression)(node.parent) ? node.parent : void 0; + if (!parentMemberExpression) { + return false; + } + if (userEventName && [userEventName, USER_EVENT_NAME].includes(node.name) || utils_1.ASTUtils.isIdentifier(parentMemberExpression.object) && parentMemberExpression.object.name === node.name) { + return false; + } + if (userEventName && utils_1.ASTUtils.isIdentifier(parentMemberExpression.object) && parentMemberExpression.object.name === userEventName) { + return true; + } + if (userEventSetupVars && utils_1.ASTUtils.isIdentifier(parentMemberExpression.object) && userEventSetupVars.has(parentMemberExpression.object.name)) { + return true; + } + return false; + }; + const isRenderUtil = (node) => isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { + if (isAggressiveRenderReportingEnabled()) { + return identifierNodeName.toLowerCase().includes(RENDER_NAME); + } + return [RENDER_NAME, ...getCustomRenders()].some((validRenderName) => validRenderName === identifierNodeName || Boolean(originalNodeName) && validRenderName === originalNodeName); + }); + const isCreateEventUtil = (node) => { + const isCreateEventCallback = (identifierNodeName, originalNodeName) => [identifierNodeName, originalNodeName].includes(CREATE_EVENT_NAME); + if ((0, node_utils_1.isCallExpression)(node) && (0, node_utils_1.isMemberExpression)(node.callee) && utils_1.ASTUtils.isIdentifier(node.callee.object)) { + return isPotentialTestingLibraryFunction(node.callee.object, isCreateEventCallback); + } + if ((0, node_utils_1.isCallExpression)(node) && (0, node_utils_1.isMemberExpression)(node.callee) && (0, node_utils_1.isMemberExpression)(node.callee.object) && utils_1.ASTUtils.isIdentifier(node.callee.object.property)) { + return isPotentialTestingLibraryFunction(node.callee.object.property, isCreateEventCallback); + } + const identifier = (0, node_utils_1.getDeepestIdentifierNode)(node); + return isPotentialTestingLibraryFunction(identifier, isCreateEventCallback); + }; + const isRenderVariableDeclarator = (node) => { + if (!node.init) { + return false; + } + const initIdentifierNode = (0, node_utils_1.getDeepestIdentifierNode)(node.init); + if (!initIdentifierNode) { + return false; + } + return isRenderUtil(initIdentifierNode); + }; + const isDebugUtil = (identifierNode, validNames = utils_2.DEBUG_UTILS) => { + const isBuiltInConsole = (0, node_utils_1.isMemberExpression)(identifierNode.parent) && utils_1.ASTUtils.isIdentifier(identifierNode.parent.object) && identifierNode.parent.object.name === "console"; + return !isBuiltInConsole && isPotentialTestingLibraryFunction(identifierNode, (identifierNodeName, originalNodeName) => { + return validNames.includes(identifierNodeName) || !!originalNodeName && validNames.includes(originalNodeName); + }); + }; + const isActUtil = (node) => { + const isTestingLibraryAct = isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { + return [identifierNodeName, originalNodeName].filter(Boolean).includes("act"); + }); + const isReactDomTestUtilsAct = (() => { + if (!importedReactDomTestUtilsNode) { + return false; + } + const referenceNode = (0, node_utils_1.getReferenceNode)(node); + const referenceNodeIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); + if (!referenceNodeIdentifier) { + return false; + } + const importedUtilSpecifier = (0, node_utils_1.findImportSpecifier)(node.name, importedReactDomTestUtilsNode); + if (!importedUtilSpecifier) { + return false; + } + const importDeclaration = (() => { + if ((0, node_utils_1.isImportDeclaration)(importedUtilSpecifier.parent)) { + return importedUtilSpecifier.parent; + } + const variableDeclarator = (0, node_utils_1.findClosestVariableDeclaratorNode)(importedUtilSpecifier); + if ((0, node_utils_1.isCallExpression)(variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init)) { + return variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init; + } + return void 0; + })(); + if (!importDeclaration) { + return false; + } + const importDeclarationName = (0, node_utils_1.getImportModuleName)(importDeclaration); + if (!importDeclarationName) { + return false; + } + if (importDeclarationName !== REACT_DOM_TEST_UTILS_PACKAGE) { + return false; + } + return (0, node_utils_1.hasImportMatch)(importedUtilSpecifier, referenceNodeIdentifier.name); + })(); + return isTestingLibraryAct || isReactDomTestUtilsAct; + }; + const isTestingLibraryUtil = (node) => { + return isAsyncUtil(node) || isQuery(node) || isRenderUtil(node) || isFireEventMethod(node) || isUserEventMethod(node) || isActUtil(node) || isCreateEventUtil(node); + }; + const isPresenceAssert = (node) => { + const { matcher, isNegated } = (0, node_utils_1.getAssertNodeInfo)(node); + if (!matcher) { + return false; + } + return isNegated ? utils_2.ABSENCE_MATCHERS.some((absenceMather) => absenceMather === matcher) : utils_2.PRESENCE_MATCHERS.some((presenceMather) => presenceMather === matcher); + }; + const isAbsenceAssert = (node) => { + const { matcher, isNegated } = (0, node_utils_1.getAssertNodeInfo)(node); + if (!matcher) { + return false; + } + return isNegated ? utils_2.PRESENCE_MATCHERS.some((presenceMather) => presenceMather === matcher) : utils_2.ABSENCE_MATCHERS.some((absenceMather) => absenceMather === matcher); + }; + const isMatchingAssert = (node, matcherName) => { + const { matcher } = (0, node_utils_1.getAssertNodeInfo)(node); + if (!matcher) { + return false; + } + return matcher === matcherName; + }; + const findImportedTestingLibraryUtilSpecifier = (specifierName) => { + var _a; + const node = (_a = getCustomModuleImportNode()) !== null && _a !== void 0 ? _a : getTestingLibraryImportNode(); + if (!node) { + return void 0; + } + return (0, node_utils_1.findImportSpecifier)(specifierName, node); + }; + const findImportedUserEventSpecifier = () => { + if (!importedUserEventLibraryNode) { + return null; + } + if ((0, node_utils_1.isImportDeclaration)(importedUserEventLibraryNode)) { + const userEventIdentifier = importedUserEventLibraryNode.specifiers.find((specifier) => (0, node_utils_1.isImportDefaultSpecifier)(specifier)); + if (userEventIdentifier) { + return userEventIdentifier.local; + } + } else { + if (!utils_1.ASTUtils.isVariableDeclarator(importedUserEventLibraryNode.parent)) { + return null; + } + const requireNode = importedUserEventLibraryNode.parent; + if (!utils_1.ASTUtils.isIdentifier(requireNode.id)) { + return null; + } + return requireNode.id; + } + return null; + }; + const getTestingLibraryImportedUtilSpecifier = (node) => { + var _a; + const identifierName = (_a = (0, node_utils_1.getPropertyIdentifierNode)(node)) === null || _a === void 0 ? void 0 : _a.name; + if (!identifierName) { + return void 0; + } + return findImportedTestingLibraryUtilSpecifier(identifierName); + }; + const canReportErrors = () => { + return skipRuleReportingCheck || isTestingLibraryImported(); + }; + const isNodeComingFromTestingLibrary = (node) => { + var _a; + const importNode = getTestingLibraryImportedUtilSpecifier(node); + if (!importNode) { + return false; + } + const referenceNode = (0, node_utils_1.getReferenceNode)(node); + const referenceNodeIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); + if (!referenceNodeIdentifier) { + return false; + } + const importDeclaration = (() => { + if ((0, node_utils_1.isImportDeclaration)(importNode.parent)) { + return importNode.parent; + } + const variableDeclarator = (0, node_utils_1.findClosestVariableDeclaratorNode)(importNode); + if ((0, node_utils_1.isCallExpression)(variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init)) { + return variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init; + } + return void 0; + })(); + if (!importDeclaration) { + return false; + } + const importDeclarationName = (0, node_utils_1.getImportModuleName)(importDeclaration); + if (!importDeclarationName) { + return false; + } + const identifierName = (_a = (0, node_utils_1.getPropertyIdentifierNode)(node)) === null || _a === void 0 ? void 0 : _a.name; + if (!identifierName) { + return false; + } + const hasImportElementMatch = (0, node_utils_1.hasImportMatch)(importNode, identifierName); + return hasImportElementMatch && (0, is_testing_library_module_1.isTestingLibraryModule)(importDeclarationName, customModuleSetting); + }; + const helpers = { + getTestingLibraryImportNode, + getAllTestingLibraryImportNodes, + getCustomModuleImportNode, + getTestingLibraryImportName, + getCustomModuleImportName, + isTestingLibraryImported, + isTestingLibraryUtil, + isGetQueryVariant, + isQueryQueryVariant, + isFindQueryVariant, + isSyncQuery, + isAsyncQuery, + isQuery, + isCustomQuery, + isBuiltInQuery, + isAsyncUtil, + isFireEventUtil, + isUserEventUtil, + isFireEventMethod, + isUserEventMethod, + isRenderUtil, + isCreateEventUtil, + isRenderVariableDeclarator, + isDebugUtil, + isActUtil, + isPresenceAssert, + isMatchingAssert, + isAbsenceAssert, + canReportErrors, + findImportedTestingLibraryUtilSpecifier, + isNodeComingFromTestingLibrary + }; + const detectionInstructions = { + ImportDeclaration(node) { + if (typeof node.source.value !== "string") { + return; + } + if ((0, is_testing_library_module_1.isOfficialTestingLibraryModule)(node.source.value)) { + importedTestingLibraryNodes.push(node); + } + const customModule = getCustomModule(); + if (!importedCustomModuleNode && (0, is_testing_library_module_1.isCustomTestingLibraryModule)(node.source.value, customModule)) { + importedCustomModuleNode = node; + } + if (!importedUserEventLibraryNode && node.source.value === utils_2.USER_EVENT_MODULE) { + importedUserEventLibraryNode = node; + } + if (!importedUserEventLibraryNode && node.source.value === REACT_DOM_TEST_UTILS_PACKAGE) { + importedReactDomTestUtilsNode = node; + } + }, + [`CallExpression > Identifier[name="require"]`](node) { + const callExpression = node.parent; + const { arguments: args } = callExpression; + if (args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === "string" && (0, is_testing_library_module_1.isOfficialTestingLibraryModule)(arg.value))) { + importedTestingLibraryNodes.push(callExpression); + } + const customModule = getCustomModule(); + if (!importedCustomModuleNode && args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === "string" && (0, is_testing_library_module_1.isCustomTestingLibraryModule)(arg.value, customModule))) { + importedCustomModuleNode = callExpression; + } + if (!importedCustomModuleNode && args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === "string" && arg.value === utils_2.USER_EVENT_MODULE)) { + importedUserEventLibraryNode = callExpression; + } + if (!importedReactDomTestUtilsNode && args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === "string" && arg.value === REACT_DOM_TEST_UTILS_PACKAGE)) { + importedReactDomTestUtilsNode = callExpression; + } + } + }; + const ruleInstructions = ruleCreate(context, optionsWithDefault, helpers); + const enhancedRuleInstructions = {}; + const allKeys = new Set(Object.keys(detectionInstructions).concat(Object.keys(ruleInstructions))); + allKeys.forEach((instruction) => { + enhancedRuleInstructions[instruction] = (node) => { + var _a, _b; + if (instruction in detectionInstructions) { + (_a = detectionInstructions[instruction]) === null || _a === void 0 ? void 0 : _a.call(detectionInstructions, node); + } + if (canReportErrors() && ruleInstructions[instruction]) { + return (_b = ruleInstructions[instruction]) === null || _b === void 0 ? void 0 : _b.call(ruleInstructions, node); + } + return void 0; + }; + }); + return enhancedRuleInstructions; + }; + } + } +}); + +// eslint-plugin-testing-library/dist/create-testing-library-rule/index.js +var require_create_testing_library_rule = __commonJS({ + "eslint-plugin-testing-library/dist/create-testing-library-rule/index.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.createTestingLibraryRule = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var utils_2 = require_utils(); + var detect_testing_library_utils_1 = require_detect_testing_library_utils(); + var createTestingLibraryRule = ({ create, detectionOptions = {}, ...remainingConfig }) => { + const rule = utils_1.ESLintUtils.RuleCreator(utils_2.getDocsUrl)({ + ...remainingConfig, + create: (0, detect_testing_library_utils_1.detectTestingLibraryUtils)(create, detectionOptions) + }); + const { docs } = rule.meta; + if (docs === void 0) { + throw new Error("Rule metadata must contain `docs` property"); + } + return { ...rule, meta: { ...rule.meta, docs } }; + }; + exports.createTestingLibraryRule = createTestingLibraryRule; + } +}); + +// eslint-plugin-testing-library/dist/utils/add-async-to-function-fix.js +var require_add_async_to_function_fix = __commonJS({ + "eslint-plugin-testing-library/dist/utils/add-async-to-function-fix.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.addAsyncToFunctionFix = void 0; + var addAsyncToFunctionFix = (fixer, ruleFix, functionExpression) => { + if (functionExpression && !functionExpression.async) { + functionExpression.async = true; + return [ruleFix, fixer.insertTextBefore(functionExpression, "async ")]; + } + return ruleFix; + }; + exports.addAsyncToFunctionFix = addAsyncToFunctionFix; + } +}); + +// eslint-plugin-testing-library/dist/rules/await-async-queries.js +var require_await_async_queries = __commonJS({ + "eslint-plugin-testing-library/dist/rules/await-async-queries.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + var add_async_to_function_fix_1 = require_add_async_to_function_fix(); + exports.RULE_NAME = "await-async-queries"; + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "problem", + docs: { + description: "Enforce promises from async queries to be handled", + recommendedConfig: { + dom: "error", + angular: "error", + react: "error", + vue: "error", + svelte: "error", + marko: "error" + } + }, + messages: { + awaitAsyncQuery: "promise returned from `{{ name }}` query must be handled", + asyncQueryWrapper: "promise returned from `{{ name }}` wrapper over async query must be handled" + }, + fixable: "code", + schema: [] + }, + defaultOptions: [], + create(context, _, helpers) { + const functionWrappersNames = []; + function detectAsyncQueryWrapper(node) { + const innerFunction = (0, node_utils_1.getInnermostReturningFunction)(context, node); + if (innerFunction) { + functionWrappersNames.push((0, node_utils_1.getFunctionName)(innerFunction)); + } + } + return { + CallExpression(node) { + const identifierNode = (0, node_utils_1.getDeepestIdentifierNode)(node); + if (!identifierNode) { + return; + } + if (helpers.isAsyncQuery(identifierNode)) { + detectAsyncQueryWrapper(identifierNode); + const closestCallExpressionNode = (0, node_utils_1.findClosestCallExpressionNode)(node, true); + if (!(closestCallExpressionNode === null || closestCallExpressionNode === void 0 ? void 0 : closestCallExpressionNode.parent)) { + return; + } + const references = (0, node_utils_1.getVariableReferences)(context, closestCallExpressionNode.parent); + if (references.length === 0) { + if (!(0, node_utils_1.isPromiseHandled)(identifierNode)) { + context.report({ + node: identifierNode, + messageId: "awaitAsyncQuery", + data: { name: identifierNode.name }, + fix: (fixer) => { + if ((0, node_utils_1.isMemberExpression)(identifierNode.parent) && utils_1.ASTUtils.isIdentifier(identifierNode.parent.object)) { + return fixer.insertTextBefore(identifierNode.parent, "await "); + } + return fixer.insertTextBefore(identifierNode, "await "); + } + }); + return; + } + } + for (const reference of references) { + if (utils_1.ASTUtils.isIdentifier(reference.identifier) && !(0, node_utils_1.isPromiseHandled)(reference.identifier)) { + context.report({ + node: identifierNode, + messageId: "awaitAsyncQuery", + data: { name: identifierNode.name }, + fix: (fixer) => references.map((ref) => fixer.insertTextBefore(ref.identifier, "await ")) + }); + return; + } + } + } else if (functionWrappersNames.includes(identifierNode.name) && !(0, node_utils_1.isPromiseHandled)(identifierNode)) { + context.report({ + node: identifierNode, + messageId: "asyncQueryWrapper", + data: { name: identifierNode.name }, + fix: (fixer) => { + const functionExpression = (0, node_utils_1.findClosestFunctionExpressionNode)(node); + if (!functionExpression) + return null; + let IdentifierNodeFixer; + if ((0, node_utils_1.isMemberExpression)(identifierNode.parent)) { + IdentifierNodeFixer = fixer.insertTextBefore(identifierNode.parent, "await "); + } else { + IdentifierNodeFixer = fixer.insertTextBefore(identifierNode, "await "); + } + return (0, add_async_to_function_fix_1.addAsyncToFunctionFix)(fixer, IdentifierNodeFixer, functionExpression); + } + }); + } + } + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/await-async-utils.js +var require_await_async_utils = __commonJS({ + "eslint-plugin-testing-library/dist/rules/await-async-utils.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + var add_async_to_function_fix_1 = require_add_async_to_function_fix(); + exports.RULE_NAME = "await-async-utils"; + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "problem", + docs: { + description: "Enforce promises from async utils to be awaited properly", + recommendedConfig: { + dom: "error", + angular: "error", + react: "error", + vue: "error", + svelte: "error", + marko: "error" + } + }, + messages: { + awaitAsyncUtil: "Promise returned from `{{ name }}` must be handled", + asyncUtilWrapper: "Promise returned from {{ name }} wrapper over async util must be handled" + }, + schema: [], + fixable: "code" + }, + defaultOptions: [], + create(context, _, helpers) { + const functionWrappersNames = []; + function detectAsyncUtilWrapper(node) { + const innerFunction = (0, node_utils_1.getInnermostReturningFunction)(context, node); + if (!innerFunction) { + return; + } + const functionName = (0, node_utils_1.getFunctionName)(innerFunction); + if (functionName.length === 0) { + return; + } + functionWrappersNames.push(functionName); + } + function detectDestructuredAsyncUtilWrapperAliases(node) { + for (const property of node.properties) { + if (!(0, node_utils_1.isProperty)(property)) { + continue; + } + if (!utils_1.ASTUtils.isIdentifier(property.key) || !utils_1.ASTUtils.isIdentifier(property.value)) { + continue; + } + if (functionWrappersNames.includes(property.key.name)) { + const isDestructuredAsyncWrapperPropertyRenamed = property.key.name !== property.value.name; + if (isDestructuredAsyncWrapperPropertyRenamed) { + functionWrappersNames.push(property.value.name); + } + } + } + } + function insertAwaitBeforeNode(fixer, node) { + return fixer.insertTextBefore(node, "await "); + } + const getMessageId = (node) => { + if (helpers.isAsyncUtil(node)) { + return "awaitAsyncUtil"; + } + return "asyncUtilWrapper"; + }; + return { + VariableDeclarator(node) { + var _a, _b; + if ((0, node_utils_1.isObjectPattern)(node.id)) { + detectDestructuredAsyncUtilWrapperAliases(node.id); + return; + } + const isAssigningKnownAsyncFunctionWrapper = utils_1.ASTUtils.isIdentifier(node.id) && node.init !== null && !(0, node_utils_1.isCallExpression)(node.init) && !utils_1.ASTUtils.isAwaitExpression(node.init) && functionWrappersNames.includes((_b = (_a = (0, node_utils_1.getDeepestIdentifierNode)(node.init)) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : ""); + if (isAssigningKnownAsyncFunctionWrapper) { + functionWrappersNames.push(node.id.name); + } + }, + CallExpression(node) { + const callExpressionIdentifier = (0, node_utils_1.getDeepestIdentifierNode)(node); + if (!callExpressionIdentifier) { + return; + } + const isAsyncUtilOrKnownAliasAroundIt = helpers.isAsyncUtil(callExpressionIdentifier) || functionWrappersNames.includes(callExpressionIdentifier.name); + if (!isAsyncUtilOrKnownAliasAroundIt) { + return; + } + if (helpers.isAsyncUtil(callExpressionIdentifier)) { + detectAsyncUtilWrapper(callExpressionIdentifier); + } + const closestCallExpression = (0, node_utils_1.findClosestCallExpressionNode)(callExpressionIdentifier, true); + if (!(closestCallExpression === null || closestCallExpression === void 0 ? void 0 : closestCallExpression.parent)) { + return; + } + const references = (0, node_utils_1.getVariableReferences)(context, closestCallExpression.parent); + const functionExpression = (0, node_utils_1.findClosestFunctionExpressionNode)(node); + if (references.length === 0) { + if (!(0, node_utils_1.isPromiseHandled)(callExpressionIdentifier)) { + context.report({ + node: callExpressionIdentifier, + messageId: getMessageId(callExpressionIdentifier), + data: { + name: callExpressionIdentifier.name + }, + fix: (fixer) => { + const referenceNode = (0, node_utils_1.getReferenceNode)(callExpressionIdentifier); + const awaitFix = insertAwaitBeforeNode(fixer, referenceNode); + return (0, add_async_to_function_fix_1.addAsyncToFunctionFix)(fixer, awaitFix, functionExpression); + } + }); + } + } else { + for (const reference of references) { + const referenceNode = reference.identifier; + if (!(0, node_utils_1.isPromiseHandled)(referenceNode)) { + context.report({ + node: callExpressionIdentifier, + messageId: getMessageId(callExpressionIdentifier), + data: { + name: callExpressionIdentifier.name + }, + fix: (fixer) => { + const awaitFix = insertAwaitBeforeNode(fixer, referenceNode); + return (0, add_async_to_function_fix_1.addAsyncToFunctionFix)(fixer, awaitFix, functionExpression); + } + }); + return; + } + } + } + } + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/no-debugging-utils.js +var require_no_debugging_utils = __commonJS({ + "eslint-plugin-testing-library/dist/rules/no-debugging-utils.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + var utils_2 = require_utils(); + exports.RULE_NAME = "no-debugging-utils"; + var defaultUtilsToCheckFor = { + debug: true, + logTestingPlaygroundURL: true, + prettyDOM: true, + logRoles: true, + logDOM: true, + prettyFormat: true + }; + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "problem", + docs: { + description: "Disallow the use of debugging utilities like `debug`", + recommendedConfig: { + dom: false, + angular: "warn", + react: "warn", + vue: "warn", + svelte: "warn", + marko: "warn" + } + }, + messages: { + noDebug: "Unexpected debug statement" + }, + schema: [ + { + type: "object", + properties: { + utilsToCheckFor: { + type: "object", + properties: utils_2.DEBUG_UTILS.reduce((obj, name) => ({ + [name]: { type: "boolean" }, + ...obj + }), {}), + additionalProperties: false + } + }, + additionalProperties: false + } + ] + }, + defaultOptions: [{ utilsToCheckFor: defaultUtilsToCheckFor }], + create(context, [{ utilsToCheckFor = {} }], helpers) { + const suspiciousDebugVariableNames = []; + const suspiciousReferenceNodes = []; + const renderWrapperNames = []; + const builtInConsoleNodes = []; + const utilsToReport = Object.entries(utilsToCheckFor).filter(([, shouldCheckFor]) => shouldCheckFor).map(([name]) => name); + function detectRenderWrapper(node) { + const innerFunction = (0, node_utils_1.getInnermostReturningFunction)(context, node); + if (innerFunction) { + renderWrapperNames.push((0, node_utils_1.getFunctionName)(innerFunction)); + } + } + return { + VariableDeclarator(node) { + if (!node.init) { + return; + } + const initIdentifierNode = (0, node_utils_1.getDeepestIdentifierNode)(node.init); + if (!initIdentifierNode) { + return; + } + if (initIdentifierNode.name === "console") { + builtInConsoleNodes.push(node); + return; + } + const isRenderWrapperVariableDeclarator = renderWrapperNames.includes(initIdentifierNode.name); + if (!helpers.isRenderVariableDeclarator(node) && !isRenderWrapperVariableDeclarator) { + return; + } + if ((0, node_utils_1.isObjectPattern)(node.id)) { + for (const property of node.id.properties) { + if ((0, node_utils_1.isProperty)(property) && utils_1.ASTUtils.isIdentifier(property.key) && utilsToReport.includes(property.key.name)) { + const identifierNode = (0, node_utils_1.getDeepestIdentifierNode)(property.value); + if (identifierNode) { + suspiciousDebugVariableNames.push(identifierNode.name); + } + } + } + } + if (utils_1.ASTUtils.isIdentifier(node.id)) { + suspiciousReferenceNodes.push(node.id); + } + }, + CallExpression(node) { + const callExpressionIdentifier = (0, node_utils_1.getDeepestIdentifierNode)(node); + if (!callExpressionIdentifier) { + return; + } + if (helpers.isRenderUtil(callExpressionIdentifier)) { + detectRenderWrapper(callExpressionIdentifier); + } + const referenceNode = (0, node_utils_1.getReferenceNode)(node); + const referenceIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); + if (!referenceIdentifier) { + return; + } + const isDebugUtil = helpers.isDebugUtil(callExpressionIdentifier, utilsToReport); + const isDeclaredDebugVariable = suspiciousDebugVariableNames.includes(callExpressionIdentifier.name); + const isChainedReferenceDebug = suspiciousReferenceNodes.some((suspiciousReferenceIdentifier) => { + return utilsToReport.includes(callExpressionIdentifier.name) && suspiciousReferenceIdentifier.name === referenceIdentifier.name; + }); + const isVariableFromBuiltInConsole = builtInConsoleNodes.some((variableDeclarator) => { + const variables = (0, utils_2.getDeclaredVariables)(context, variableDeclarator); + return variables.some(({ name }) => name === callExpressionIdentifier.name && (0, node_utils_1.isCallExpression)(callExpressionIdentifier.parent)); + }); + if (!isVariableFromBuiltInConsole && (isDebugUtil || isDeclaredDebugVariable || isChainedReferenceDebug)) { + context.report({ + node: callExpressionIdentifier, + messageId: "noDebug" + }); + } + } + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/no-manual-cleanup.js +var require_no_manual_cleanup = __commonJS({ + "eslint-plugin-testing-library/dist/rules/no-manual-cleanup.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + var utils_2 = require_utils(); + exports.RULE_NAME = "no-manual-cleanup"; + var CLEANUP_LIBRARY_REGEXP = /(@testing-library\/(preact|react|svelte|vue))|@marko\/testing-library/; + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "problem", + docs: { + description: "Disallow the use of `cleanup`", + recommendedConfig: { + dom: false, + angular: false, + react: "error", + vue: "error", + svelte: "error", + marko: false + } + }, + messages: { + noManualCleanup: "`cleanup` is performed automatically by your test runner, you don't need manual cleanups." + }, + schema: [] + }, + defaultOptions: [], + create(context, _, helpers) { + function reportImportReferences(references) { + for (const reference of references) { + const utilsUsage = reference.identifier.parent; + if (utilsUsage && (0, node_utils_1.isMemberExpression)(utilsUsage) && utils_1.ASTUtils.isIdentifier(utilsUsage.property) && utilsUsage.property.name === "cleanup") { + context.report({ + node: utilsUsage.property, + messageId: "noManualCleanup" + }); + } + } + } + function reportCandidateModule(moduleNode) { + if ((0, node_utils_1.isImportDeclaration)(moduleNode)) { + if ((0, node_utils_1.isImportDefaultSpecifier)(moduleNode.specifiers[0])) { + const { references } = (0, utils_2.getDeclaredVariables)(context, moduleNode)[0]; + reportImportReferences(references); + } + const cleanupSpecifier = moduleNode.specifiers.find((specifier) => (0, node_utils_1.isImportSpecifier)(specifier) && utils_1.ASTUtils.isIdentifier(specifier.imported) && specifier.imported.name === "cleanup"); + if (cleanupSpecifier) { + context.report({ + node: cleanupSpecifier, + messageId: "noManualCleanup" + }); + } + } else { + const declaratorNode = moduleNode.parent; + if ((0, node_utils_1.isObjectPattern)(declaratorNode.id)) { + const cleanupProperty = declaratorNode.id.properties.find((property) => (0, node_utils_1.isProperty)(property) && utils_1.ASTUtils.isIdentifier(property.key) && property.key.name === "cleanup"); + if (cleanupProperty) { + context.report({ + node: cleanupProperty, + messageId: "noManualCleanup" + }); + } + } else { + const references = (0, node_utils_1.getVariableReferences)(context, declaratorNode); + reportImportReferences(references); + } + } + } + return { + "Program:exit"() { + const customModuleImportNode = helpers.getCustomModuleImportNode(); + for (const testingLibraryImportNode of helpers.getAllTestingLibraryImportNodes()) { + const testingLibraryImportName = (0, node_utils_1.getImportModuleName)(testingLibraryImportNode); + if (testingLibraryImportName === null || testingLibraryImportName === void 0 ? void 0 : testingLibraryImportName.match(CLEANUP_LIBRARY_REGEXP)) { + reportCandidateModule(testingLibraryImportNode); + } + } + if (customModuleImportNode) { + reportCandidateModule(customModuleImportNode); + } + } + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/no-unnecessary-act.js +var require_no_unnecessary_act = __commonJS({ + "eslint-plugin-testing-library/dist/rules/no-unnecessary-act.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + exports.RULE_NAME = "no-unnecessary-act"; + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "problem", + docs: { + description: "Disallow wrapping Testing Library utils or empty callbacks in `act`", + recommendedConfig: { + dom: false, + angular: false, + react: "error", + vue: false, + svelte: false, + marko: "error" + } + }, + messages: { + noUnnecessaryActTestingLibraryUtil: "Avoid wrapping Testing Library util calls in `act`", + noUnnecessaryActEmptyFunction: "Avoid wrapping empty function in `act`" + }, + schema: [ + { + type: "object", + properties: { + isStrict: { + type: "boolean" + } + }, + additionalProperties: false + } + ] + }, + defaultOptions: [ + { + isStrict: true + } + ], + create(context, [{ isStrict = true }], helpers) { + function getStatementIdentifier(statement) { + const callExpression = (0, node_utils_1.getStatementCallExpression)(statement); + if (!callExpression && !(0, node_utils_1.isExpressionStatement)(statement) && !(0, node_utils_1.isReturnStatement)(statement)) { + return null; + } + if (callExpression) { + return (0, node_utils_1.getDeepestIdentifierNode)(callExpression); + } + if ((0, node_utils_1.isExpressionStatement)(statement) && utils_1.ASTUtils.isAwaitExpression(statement.expression)) { + return (0, node_utils_1.getPropertyIdentifierNode)(statement.expression.argument); + } + if ((0, node_utils_1.isReturnStatement)(statement) && statement.argument) { + return (0, node_utils_1.getPropertyIdentifierNode)(statement.argument); + } + return null; + } + function hasSomeNonTestingLibraryCall(statements) { + return statements.some((statement) => { + const identifier = getStatementIdentifier(statement); + if (!identifier) { + return false; + } + return !helpers.isTestingLibraryUtil(identifier); + }); + } + function hasTestingLibraryCall(statements) { + return statements.some((statement) => { + const identifier = getStatementIdentifier(statement); + if (!identifier) { + return false; + } + return helpers.isTestingLibraryUtil(identifier); + }); + } + function checkNoUnnecessaryActFromBlockStatement(blockStatementNode) { + const functionNode = blockStatementNode.parent; + const callExpressionNode = functionNode === null || functionNode === void 0 ? void 0 : functionNode.parent; + if (!callExpressionNode || !functionNode) { + return; + } + const identifierNode = (0, node_utils_1.getDeepestIdentifierNode)(callExpressionNode); + if (!identifierNode) { + return; + } + if (!helpers.isActUtil(identifierNode)) { + return; + } + if ((0, node_utils_1.isEmptyFunction)(functionNode)) { + context.report({ + node: identifierNode, + messageId: "noUnnecessaryActEmptyFunction" + }); + return; + } + const shouldBeReported = isStrict ? hasTestingLibraryCall(blockStatementNode.body) : !hasSomeNonTestingLibraryCall(blockStatementNode.body); + if (shouldBeReported) { + context.report({ + node: identifierNode, + messageId: "noUnnecessaryActTestingLibraryUtil" + }); + } + } + function checkNoUnnecessaryActFromImplicitReturn(node) { + var _a; + const nodeIdentifier = (0, node_utils_1.getDeepestIdentifierNode)(node); + if (!nodeIdentifier) { + return; + } + const parentCallExpression = (_a = node.parent) === null || _a === void 0 ? void 0 : _a.parent; + if (!parentCallExpression) { + return; + } + const identifierNode = (0, node_utils_1.getDeepestIdentifierNode)(parentCallExpression); + if (!identifierNode) { + return; + } + if (!helpers.isActUtil(identifierNode)) { + return; + } + if (!helpers.isTestingLibraryUtil(nodeIdentifier)) { + return; + } + context.report({ + node: identifierNode, + messageId: "noUnnecessaryActTestingLibraryUtil" + }); + } + return { + "CallExpression > ArrowFunctionExpression > BlockStatement": checkNoUnnecessaryActFromBlockStatement, + "CallExpression > FunctionExpression > BlockStatement": checkNoUnnecessaryActFromBlockStatement, + "CallExpression > ArrowFunctionExpression > CallExpression": checkNoUnnecessaryActFromImplicitReturn + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/prefer-find-by.js +var require_prefer_find_by = __commonJS({ + "eslint-plugin-testing-library/dist/rules/prefer-find-by.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + exports.getFindByQueryVariant = getFindByQueryVariant; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + var utils_2 = require_utils(); + exports.RULE_NAME = "prefer-find-by"; + function getFindByQueryVariant(queryMethod) { + return queryMethod.includes("All") ? "findAllBy" : "findBy"; + } + function findRenderDefinitionDeclaration(scope, query) { + var _a; + if (!scope) { + return null; + } + const variable = scope.variables.find((v) => v.name === query); + if (variable) { + return (_a = variable.defs.map(({ name }) => name).filter(utils_1.ASTUtils.isIdentifier).find(({ name }) => name === query)) !== null && _a !== void 0 ? _a : null; + } + return findRenderDefinitionDeclaration(scope.upper, query); + } + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "suggestion", + docs: { + description: "Suggest using `find(All)By*` query instead of `waitFor` + `get(All)By*` to wait for elements", + recommendedConfig: { + dom: "error", + angular: "error", + react: "error", + vue: "error", + svelte: "error", + marko: "error" + } + }, + messages: { + preferFindBy: "Prefer `{{queryVariant}}{{queryMethod}}` query over using `waitFor` + `{{prevQuery}}`" + }, + fixable: "code", + schema: [] + }, + defaultOptions: [], + create(context, _, helpers) { + const sourceCode = (0, utils_2.getSourceCode)(context); + function reportInvalidUsage(node, replacementParams) { + const { queryMethod, queryVariant, prevQuery, fix } = replacementParams; + context.report({ + node, + messageId: "preferFindBy", + data: { + queryVariant, + queryMethod, + prevQuery + }, + fix + }); + } + function getWrongQueryNameInAssertion(node) { + if (!(0, node_utils_1.isCallExpression)(node.body) || !(0, node_utils_1.isMemberExpression)(node.body.callee)) { + return null; + } + if ((0, node_utils_1.isCallExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.arguments[0]) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.arguments[0].callee)) { + return node.body.callee.object.arguments[0].callee; + } + if (!utils_1.ASTUtils.isIdentifier(node.body.callee.property)) { + return null; + } + if ((0, node_utils_1.isCallExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.arguments[0]) && (0, node_utils_1.isMemberExpression)(node.body.callee.object.arguments[0].callee) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.arguments[0].callee.property)) { + return node.body.callee.object.arguments[0].callee.property; + } + if ((0, node_utils_1.isMemberExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object.arguments[0]) && (0, node_utils_1.isMemberExpression)(node.body.callee.object.object.arguments[0].callee) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.object.arguments[0].callee.property)) { + return node.body.callee.object.object.arguments[0].callee.property; + } + if ((0, node_utils_1.isMemberExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object.arguments[0]) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.object.arguments[0].callee)) { + return node.body.callee.object.object.arguments[0].callee; + } + return node.body.callee.property; + } + function getWrongQueryName(node) { + if (!(0, node_utils_1.isCallExpression)(node.body)) { + return null; + } + if (utils_1.ASTUtils.isIdentifier(node.body.callee) && helpers.isSyncQuery(node.body.callee)) { + return node.body.callee; + } + return getWrongQueryNameInAssertion(node); + } + function getCaller(node) { + if (!(0, node_utils_1.isCallExpression)(node.body) || !(0, node_utils_1.isMemberExpression)(node.body.callee)) { + return null; + } + if (utils_1.ASTUtils.isIdentifier(node.body.callee.object)) { + return node.body.callee.object.name; + } + if ((0, node_utils_1.isCallExpression)(node.body.callee.object) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.callee) && (0, node_utils_1.isCallExpression)(node.body.callee.object.arguments[0]) && (0, node_utils_1.isMemberExpression)(node.body.callee.object.arguments[0].callee) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.arguments[0].callee.object)) { + return node.body.callee.object.arguments[0].callee.object.name; + } + if ((0, node_utils_1.isMemberExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object.arguments[0]) && (0, node_utils_1.isMemberExpression)(node.body.callee.object.object.arguments[0].callee) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.object.arguments[0].callee.object)) { + return node.body.callee.object.object.arguments[0].callee.object.name; + } + return null; + } + function isSyncQuery(node) { + if (!(0, node_utils_1.isCallExpression)(node.body)) { + return false; + } + const isQuery = utils_1.ASTUtils.isIdentifier(node.body.callee) && helpers.isSyncQuery(node.body.callee); + const isWrappedInPresenceAssert = (0, node_utils_1.isMemberExpression)(node.body.callee) && (0, node_utils_1.isCallExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.arguments[0]) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.arguments[0].callee) && helpers.isSyncQuery(node.body.callee.object.arguments[0].callee) && helpers.isPresenceAssert(node.body.callee); + const isWrappedInNegatedPresenceAssert = (0, node_utils_1.isMemberExpression)(node.body.callee) && (0, node_utils_1.isMemberExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object.arguments[0]) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.object.arguments[0].callee) && helpers.isSyncQuery(node.body.callee.object.object.arguments[0].callee) && helpers.isPresenceAssert(node.body.callee.object); + return isQuery || isWrappedInPresenceAssert || isWrappedInNegatedPresenceAssert; + } + function isScreenSyncQuery(node) { + if (!(0, node_utils_1.isArrowFunctionExpression)(node) || !(0, node_utils_1.isCallExpression)(node.body)) { + return false; + } + if (!(0, node_utils_1.isMemberExpression)(node.body.callee) || !utils_1.ASTUtils.isIdentifier(node.body.callee.property)) { + return false; + } + if (!utils_1.ASTUtils.isIdentifier(node.body.callee.object) && !(0, node_utils_1.isCallExpression)(node.body.callee.object) && !(0, node_utils_1.isMemberExpression)(node.body.callee.object)) { + return false; + } + const isWrappedInPresenceAssert = helpers.isPresenceAssert(node.body.callee) && (0, node_utils_1.isCallExpression)(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.arguments[0]) && (0, node_utils_1.isMemberExpression)(node.body.callee.object.arguments[0].callee) && utils_1.ASTUtils.isIdentifier(node.body.callee.object.arguments[0].callee.object); + const isWrappedInNegatedPresenceAssert = (0, node_utils_1.isMemberExpression)(node.body.callee.object) && helpers.isPresenceAssert(node.body.callee.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object) && (0, node_utils_1.isCallExpression)(node.body.callee.object.object.arguments[0]) && (0, node_utils_1.isMemberExpression)(node.body.callee.object.object.arguments[0].callee); + return helpers.isSyncQuery(node.body.callee.property) || isWrappedInPresenceAssert || isWrappedInNegatedPresenceAssert; + } + function getQueryArguments(node) { + if ((0, node_utils_1.isMemberExpression)(node.callee) && (0, node_utils_1.isCallExpression)(node.callee.object) && (0, node_utils_1.isCallExpression)(node.callee.object.arguments[0])) { + return node.callee.object.arguments[0].arguments; + } + if ((0, node_utils_1.isMemberExpression)(node.callee) && (0, node_utils_1.isMemberExpression)(node.callee.object) && (0, node_utils_1.isCallExpression)(node.callee.object.object) && (0, node_utils_1.isCallExpression)(node.callee.object.object.arguments[0])) { + return node.callee.object.object.arguments[0].arguments; + } + return node.arguments; + } + return { + "AwaitExpression > CallExpression"(node) { + var _a; + if (!utils_1.ASTUtils.isIdentifier(node.callee) || !helpers.isAsyncUtil(node.callee, ["waitFor"])) { + return; + } + const argument = node.arguments[0]; + if (!(0, node_utils_1.isArrowFunctionExpression)(argument)) { + return; + } + if ((0, node_utils_1.isBlockStatement)(argument.body) && argument.async) { + const { body } = argument.body; + const declarations = (_a = body.filter(node_utils_1.isVariableDeclaration)) === null || _a === void 0 ? void 0 : _a.flatMap((declaration) => declaration.declarations); + const findByDeclarator = declarations.find((declaration) => { + if (!utils_1.ASTUtils.isAwaitExpression(declaration.init) || !(0, node_utils_1.isCallExpression)(declaration.init.argument)) { + return false; + } + const { callee } = declaration.init.argument; + const node2 = (0, node_utils_1.getDeepestIdentifierNode)(callee); + return node2 ? helpers.isFindQueryVariant(node2) : false; + }); + const init = utils_1.ASTUtils.isAwaitExpression(findByDeclarator === null || findByDeclarator === void 0 ? void 0 : findByDeclarator.init) ? findByDeclarator.init.argument : null; + if (!(0, node_utils_1.isCallExpression)(init)) { + return; + } + const queryIdentifier = (0, node_utils_1.getDeepestIdentifierNode)(init.callee); + if (!queryIdentifier || !helpers.isAsyncQuery(queryIdentifier)) { + return; + } + const fullQueryMethod2 = queryIdentifier.name; + const queryMethod2 = fullQueryMethod2.split("By")[1]; + const queryVariant2 = getFindByQueryVariant(fullQueryMethod2); + reportInvalidUsage(node, { + queryMethod: queryMethod2, + queryVariant: queryVariant2, + prevQuery: fullQueryMethod2, + fix(fixer) { + const { parent: expressionStatement } = node.parent; + const bodyText = sourceCode.getText(argument.body).slice(1, -1).trim(); + const { line, column } = expressionStatement.loc.start; + const indent = sourceCode.getLines()[line - 1].slice(0, column); + const newText = bodyText.split("\n").map((line2) => line2.trim()).join(` +${indent}`); + return fixer.replaceText(expressionStatement, newText); + } + }); + return; + } + if (!(0, node_utils_1.isCallExpression)(argument.body)) { + return; + } + if (isScreenSyncQuery(argument)) { + const caller = getCaller(argument); + if (!caller) { + return; + } + const fullQueryMethodNode2 = getWrongQueryName(argument); + if (!fullQueryMethodNode2) { + return; + } + const fullQueryMethod2 = fullQueryMethodNode2.name; + const waitOptions = node.arguments[1]; + let waitOptionsSourceCode = ""; + if ((0, node_utils_1.isObjectExpression)(waitOptions)) { + waitOptionsSourceCode = `, ${sourceCode.getText(waitOptions)}`; + } + const queryVariant2 = getFindByQueryVariant(fullQueryMethod2); + const callArguments2 = getQueryArguments(argument.body); + const queryMethod2 = fullQueryMethod2.split("By")[1]; + if (!queryMethod2) { + return; + } + reportInvalidUsage(node, { + queryMethod: queryMethod2, + queryVariant: queryVariant2, + prevQuery: fullQueryMethod2, + fix(fixer) { + const property = argument.body.callee.property; + if (helpers.isCustomQuery(property)) { + return null; + } + const newCode = `${caller}.${queryVariant2}${queryMethod2}(${callArguments2.map((callArgNode) => sourceCode.getText(callArgNode)).join(", ")}${waitOptionsSourceCode})`; + return fixer.replaceText(node, newCode); + } + }); + return; + } + if (!isSyncQuery(argument)) { + return; + } + const fullQueryMethodNode = getWrongQueryName(argument); + if (!fullQueryMethodNode) { + return; + } + const fullQueryMethod = fullQueryMethodNode.name; + const queryMethod = fullQueryMethod.split("By")[1]; + const queryVariant = getFindByQueryVariant(fullQueryMethod); + const callArguments = getQueryArguments(argument.body); + reportInvalidUsage(node, { + queryMethod, + queryVariant, + prevQuery: fullQueryMethod, + fix(fixer) { + if (helpers.isCustomQuery(argument.body.callee)) { + return null; + } + const findByMethod = `${queryVariant}${queryMethod}`; + const allFixes = []; + const newCode = `${findByMethod}(${callArguments.map((callArgNode) => sourceCode.getText(callArgNode)).join(", ")})`; + allFixes.push(fixer.replaceText(node, newCode)); + const definition = findRenderDefinitionDeclaration((0, utils_2.getScope)(context, fullQueryMethodNode), fullQueryMethod); + if (!definition) { + return allFixes; + } + if (definition.parent && (0, node_utils_1.isObjectPattern)(definition.parent.parent)) { + const allVariableDeclarations = definition.parent.parent; + if (allVariableDeclarations.properties.some((p) => (0, node_utils_1.isProperty)(p) && utils_1.ASTUtils.isIdentifier(p.key) && p.key.name === findByMethod)) { + return allFixes; + } + const textDestructuring = sourceCode.getText(allVariableDeclarations); + const text = textDestructuring.replace(/(\s*})$/, `, ${findByMethod}$1`); + allFixes.push(fixer.replaceText(allVariableDeclarations, text)); + } + return allFixes; + } + }); + } + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/prefer-presence-queries.js +var require_prefer_presence_queries = __commonJS({ + "eslint-plugin-testing-library/dist/rules/prefer-presence-queries.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + exports.RULE_NAME = "prefer-presence-queries"; + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + docs: { + description: "Ensure appropriate `get*`/`query*` queries are used with their respective matchers", + recommendedConfig: { + dom: "error", + angular: "error", + react: "error", + vue: "error", + svelte: "error", + marko: "error" + } + }, + messages: { + wrongPresenceQuery: "Use `getBy*` queries rather than `queryBy*` for checking element is present", + wrongAbsenceQuery: "Use `queryBy*` queries rather than `getBy*` for checking element is NOT present" + }, + fixable: "code", + schema: [ + { + type: "object", + additionalProperties: false, + properties: { + presence: { + type: "boolean" + }, + absence: { + type: "boolean" + } + } + } + ], + type: "suggestion" + }, + defaultOptions: [ + { + presence: true, + absence: true + } + ], + create(context, [{ absence = true, presence = true }], helpers) { + return { + "CallExpression Identifier"(node) { + const expectCallNode = (0, node_utils_1.findClosestCallNode)(node, "expect"); + const withinCallNode = (0, node_utils_1.findClosestCallNode)(node, "within"); + if (!(0, node_utils_1.isMemberExpression)(expectCallNode === null || expectCallNode === void 0 ? void 0 : expectCallNode.parent)) { + return; + } + if (!helpers.isSyncQuery(node)) { + return; + } + const isPresenceQuery = helpers.isGetQueryVariant(node); + const expectStatement = expectCallNode.parent; + const isPresenceAssert = helpers.isPresenceAssert(expectStatement); + const isAbsenceAssert = helpers.isAbsenceAssert(expectStatement); + if (!isPresenceAssert && !isAbsenceAssert) { + return; + } + if (presence && (withinCallNode || isPresenceAssert) && !isPresenceQuery) { + const newQueryName = node.name.replace(/^query/, "get"); + context.report({ + node, + messageId: "wrongPresenceQuery", + fix: (fixer) => fixer.replaceText(node, newQueryName) + }); + } else if (!withinCallNode && absence && isAbsenceAssert && isPresenceQuery) { + const newQueryName = node.name.replace(/^get/, "query"); + context.report({ + node, + messageId: "wrongAbsenceQuery", + fix: (fixer) => fixer.replaceText(node, newQueryName) + }); + } + } + }; + } + }); + } +}); + +// eslint-plugin-testing-library/dist/rules/prefer-screen-queries.js +var require_prefer_screen_queries = __commonJS({ + "eslint-plugin-testing-library/dist/rules/prefer-screen-queries.js"(exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); + exports.RULE_NAME = void 0; + var utils_1 = __require("./syntax-rule-utilities.mjs"); + var create_testing_library_rule_1 = require_create_testing_library_rule(); + var node_utils_1 = require_node_utils(); + exports.RULE_NAME = "prefer-screen-queries"; + var ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING = [ + "container", + "baseElement" + ]; + function usesContainerOrBaseElement(node) { + const secondArgument = node.arguments[1]; + return (0, node_utils_1.isObjectExpression)(secondArgument) && secondArgument.properties.some((property) => (0, node_utils_1.isProperty)(property) && utils_1.ASTUtils.isIdentifier(property.key) && ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING.includes(property.key.name)); + } + exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ + name: exports.RULE_NAME, + meta: { + type: "suggestion", + docs: { + description: "Suggest using `screen` while querying", + recommendedConfig: { + dom: "error", + angular: "error", + react: "error", + vue: "error", + svelte: "error", + marko: "error" + } + }, + messages: { + preferScreenQueries: "Avoid destructuring queries from `render` result, use `screen.{{ name }}` instead" + }, + schema: [] + }, + defaultOptions: [], + create(context, _, helpers) { + const renderWrapperNames = []; + function detectRenderWrapper(node) { + const innerFunction = (0, node_utils_1.getInnermostReturningFunction)(context, node); + if (innerFunction) { + renderWrapperNames.push((0, node_utils_1.getFunctionName)(innerFunction)); + } + } + function isReportableRender(node) { + return helpers.isRenderUtil(node) || renderWrapperNames.includes(node.name); + } + function reportInvalidUsage(node) { + context.report({ + node, + messageId: "preferScreenQueries", + data: { + name: node.name + } + }); + } + function saveSafeDestructuredQueries(node) { + if ((0, node_utils_1.isObjectPattern)(node.id)) { + for (const property of node.id.properties) { + if ((0, node_utils_1.isProperty)(property) && utils_1.ASTUtils.isIdentifier(property.key) && helpers.isBuiltInQuery(property.key)) { + safeDestructuredQueries.push(property.key.name); + } + } + } + } + function isIdentifierAllowed(name) { + return ["screen", ...withinDeclaredVariables].includes(name); + } + const safeDestructuredQueries = []; + const withinDeclaredVariables = []; + return { + VariableDeclarator(node) { + if (!(0, node_utils_1.isCallExpression)(node.init) || !utils_1.ASTUtils.isIdentifier(node.init.callee)) { + return; + } + const isComingFromValidRender = isReportableRender(node.init.callee); + if (!isComingFromValidRender) { + saveSafeDestructuredQueries(node); + } + const isWithinFunction = node.init.callee.name === "within"; + const usesRenderOptions = isComingFromValidRender && usesContainerOrBaseElement(node.init); + if (!isWithinFunction && !usesRenderOptions) { + return; + } + if ((0, node_utils_1.isObjectPattern)(node.id)) { + saveSafeDestructuredQueries(node); + } else if (utils_1.ASTUtils.isIdentifier(node.id)) { + withinDeclaredVariables.push(node.id.name); + } + }, + CallExpression(node) { + const identifierNode = (0, node_utils_1.getDeepestIdentifierNode)(node); + if (!identifierNode) { + return; + } + if (helpers.isRenderUtil(identifierNode)) { + detectRenderWrapper(identifierNode); + } + if (!helpers.isBuiltInQuery(identifierNode)) { + return; + } + if (!(0, node_utils_1.isMemberExpression)(identifierNode.parent)) { + const isSafeDestructuredQuery = safeDestructuredQueries.some((queryName) => queryName === identifierNode.name); + if (isSafeDestructuredQuery) { + return; + } + reportInvalidUsage(identifierNode); + return; + } + const memberExpressionNode = identifierNode.parent; + if ((0, node_utils_1.isCallExpression)(memberExpressionNode.object) && utils_1.ASTUtils.isIdentifier(memberExpressionNode.object.callee) && memberExpressionNode.object.callee.name !== "within" && isReportableRender(memberExpressionNode.object.callee) && !usesContainerOrBaseElement(memberExpressionNode.object)) { + reportInvalidUsage(identifierNode); + return; + } + if (utils_1.ASTUtils.isIdentifier(memberExpressionNode.object) && !isIdentifierAllowed(memberExpressionNode.object.name)) { + reportInvalidUsage(identifierNode); + } + } + }; + } + }); + } +}); + +// testing-library-entry.mjs +var import_await_async_queries = __toESM(require_await_async_queries(), 1); +var import_await_async_utils = __toESM(require_await_async_utils(), 1); +var import_no_debugging_utils = __toESM(require_no_debugging_utils(), 1); +var import_no_manual_cleanup = __toESM(require_no_manual_cleanup(), 1); +var import_no_unnecessary_act = __toESM(require_no_unnecessary_act(), 1); +var import_prefer_find_by = __toESM(require_prefer_find_by(), 1); +var import_prefer_presence_queries = __toESM(require_prefer_presence_queries(), 1); +var import_prefer_screen_queries = __toESM(require_prefer_screen_queries(), 1); +var testing_library_entry_default = { rules: { "await-async-queries": import_await_async_queries.default.default, "await-async-utils": import_await_async_utils.default.default, "no-debugging-utils": import_no_debugging_utils.default.default, "no-manual-cleanup": import_no_manual_cleanup.default.default, "no-unnecessary-act": import_no_unnecessary_act.default.default, "prefer-find-by": import_prefer_find_by.default.default, "prefer-presence-queries": import_prefer_presence_queries.default.default, "prefer-screen-queries": import_prefer_screen_queries.default.default } }; +export { + testing_library_entry_default as default +}; diff --git a/contributingGuides/LINTING.md b/contributingGuides/LINTING.md index 09c5f16feaec..0752231d503c 100644 --- a/contributingGuides/LINTING.md +++ b/contributingGuides/LINTING.md @@ -35,7 +35,7 @@ By default the wrapper only prints blocking errors — seatbelt-grandfathered vi ## How the rules run - **Native rules** come from oxlint itself; type-aware ones run through `oxlint-tsgolint`. -- **JS plugins** in [`config/oxlint/plugins/`](../config/oxlint/plugins/) run the rules oxlint has no native version of, using the ESLint plugins' own rule code (`eslint`, `eslint-config-expensify`, `eslint-plugin-react`, `typescript-eslint`, ...). That is why those packages stay in `devDependencies`. The repo's own rules live in [`eslint-plugin-local-rules/`](../eslint-plugin-local-rules/) and load under the `rulesdir/` prefix. +- **JS plugins** in [`config/oxlint/plugins/`](../config/oxlint/plugins/) run the rules oxlint has no native version of, using ESLint's core rules, installed React/import/JSDoc plugins, and the attributed syntax-only rule sources in [`config/oxlint/vendor/`](../config/oxlint/vendor/README.md). These sources do not install or load a JavaScript TypeScript compiler. The repo's own rules live in [`eslint-plugin-local-rules/`](../eslint-plugin-local-rules/) and load under the `rulesdir/` prefix. - **React Compiler rules** (`react-hooks/refs`, `react-hooks/purity`, ...) come from the Rust React Compiler via [`config/oxlint/reactCompilerRust.mjs`](../config/oxlint/reactCompilerRust.mjs). - Findings, the seatbelt baseline and `eslint-disable` comments all use the ESLint rule IDs (`@typescript-eslint/no-deprecated`, `react/jsx-no-bind`, ...); [`config/oxlint/ruleNames.mjs`](../config/oxlint/ruleNames.mjs) maps oxlint's names to them. `eslint-disable-next-line ` is still the way to suppress a finding inline. - oxlint runs JS plugins on one thread, so `scripts/lint/` splits the files into shards (`--shards=` or `auto`). CI uses `--shards=4`. diff --git a/cspell.json b/cspell.json index 328ce29c1c57..e7d9f90540b4 100644 --- a/cspell.json +++ b/cspell.json @@ -1246,6 +1246,8 @@ "JFIF" ], "ignorePaths": [ + "config/oxlint/vendor/{expensify-rules,naming-convention,storybook,syntax-rule-utilities,testing-library}.mjs", + "config/oxlint/vendor/*-LICENSE", ".github/actions/javascript/**/index.js", ".gitignore", "tests/unit/__snapshots__/OptionsListUtilsSearchParityTest.ts.snap", diff --git a/oxlint.config.mts b/oxlint.config.mts index 681b0a8fb517..7c7f529941b4 100644 --- a/oxlint.config.mts +++ b/oxlint.config.mts @@ -245,6 +245,8 @@ export default defineConfig({ '**/*.config.js', '**/*.config.mjs', '**/node_modules/**/*', + // These are attributed copies of third-party rules, rebuilt with config/oxlint/vendor/build.mjs. + 'config/oxlint/vendor/{expensify-rules,naming-convention,storybook,syntax-rule-utilities,testing-library}.mjs', '**/dist/**/*', 'server/**/dist/**', 'server/victory-chart-renderer/.dev/**', @@ -463,10 +465,6 @@ export default defineConfig({ 'rulesdir/no-unsafe-onyx-read': 'error', 'rulesdir/no-onyx-get-snapshot-key': 'error', 'rulesdir/use-double-negation-instead-of-boolean': 'error', - // Needs the type of the `&&` left operand, and jsPlugins get no type info. - 'rulesdir/boolean-conditional-rendering': 'off', - // Needs typeChecker.isArrayType; `unicorn/prefer-at` stands in. - 'rulesdir/prefer-at': 'off', // ESLint's copy needs the type checker; a type-free rewrite runs for src/**/*.ts(x) below. 'rulesdir/prefer-locale-compare-from-context': 'off', // config/oxlint/reactCompilerGate.mjs drops its reports in files React Compiler memoizes. @@ -1149,7 +1147,7 @@ export default defineConfig({ 'testing-library/prefer-presence-queries': 'error', 'testing-library/prefer-screen-queries': 'error', }, - jsPlugins: ['@dword-design/eslint-plugin-import-alias', 'eslint-plugin-testing-library'], + jsPlugins: ['@dword-design/eslint-plugin-import-alias', {name: 'testing-library', specifier: './config/oxlint/vendor/testing-library.mjs'}], }, { files: ['**/*.{ts,tsx}'], diff --git a/patches/@typescript-eslint/eslint-plugin/@typescript-eslint+eslint-plugin+8.58.1+001+require-typescript6.patch b/patches/@typescript-eslint/eslint-plugin/@typescript-eslint+eslint-plugin+8.58.1+001+require-typescript6.patch deleted file mode 100644 index 9ace7759785f..000000000000 --- a/patches/@typescript-eslint/eslint-plugin/@typescript-eslint+eslint-plugin+8.58.1+001+require-typescript6.patch +++ /dev/null @@ -1,715 +0,0 @@ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-return.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-return.js -index 73fa424..a3931d0 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-return.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-return.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); - const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('consistent-return'); -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-assertions.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-assertions.js -index fb941d4..a9b4a80 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-assertions.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-assertions.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const getWrappedCode_1 = require("../util/getWrappedCode"); - exports.default = (0, util_1.createRule)({ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-exports.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-exports.js -index 282f947..b30ceca 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-exports.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/consistent-type-exports.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'consistent-type-exports', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/dot-notation.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/dot-notation.js -index 6101acd..9a791b7 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/dot-notation.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/dot-notation.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const getESLintCoreRule_1 = require("../util/getESLintCoreRule"); - const baseRule = (0, getESLintCoreRule_1.getESLintCoreRule)('dot-notation'); -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/enum-utils/shared.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/enum-utils/shared.js -index c8ea8d2..d6794e1 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/enum-utils/shared.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/enum-utils/shared.js -@@ -37,7 +37,7 @@ exports.getEnumLiterals = getEnumLiterals; - exports.getEnumTypes = getEnumTypes; - exports.getEnumKeyForLiteral = getEnumKeyForLiteral; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../../util"); - /* - * If passed an enum member, returns the type of the parent. Otherwise, -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-base-to-string.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-base-to-string.js -index f9e0ae6..dda8f25 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-base-to-string.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-base-to-string.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - var Usefulness; - (function (Usefulness) { -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-void-expression.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-void-expression.js -index da39922..1d5706a 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-void-expression.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-confusing-void-expression.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const getParentFunctionNode_1 = require("../util/getParentFunctionNode"); - exports.default = (0, util_1.createRule)({ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-deprecated.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-deprecated.js -index ed7256c..b44e193 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-deprecated.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-deprecated.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'no-deprecated', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-duplicate-type-constituents.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-duplicate-type-constituents.js -index acb785c..aac681a 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-duplicate-type-constituents.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-duplicate-type-constituents.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const astIgnoreKeys = new Set(['loc', 'parent', 'range']); - const isSameAstNode = (actualNode, expectedNode) => { -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-for-in-array.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-for-in-array.js -index 95f3e8b..5160f63 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-for-in-array.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-for-in-array.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const getForStatementHeadLoc_1 = require("../util/getForStatementHeadLoc"); - exports.default = (0, util_1.createRule)({ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-implied-eval.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-implied-eval.js -index 52c745e..047f2e2 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-implied-eval.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-implied-eval.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const FUNCTION_CONSTRUCTOR = 'Function'; - const GLOBAL_CANDIDATES = new Set(['global', 'globalThis', 'window']); -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-void-operator.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-void-operator.js -index a0ff0c9..19ed577 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-void-operator.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-meaningless-void-operator.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'no-meaningless-void-operator', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-promises.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-promises.js -index ba6af96..f07b4d8 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-promises.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-promises.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const promiseUtils_1 = require("../util/promiseUtils"); - function parseChecksVoidReturn(checksVoidReturn) { -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-spread.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-spread.js -index 6384187..8aee751 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-spread.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-misused-spread.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'no-misused-spread', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-mixed-enums.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-mixed-enums.js -index 91e064a..19508b3 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-mixed-enums.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-mixed-enums.js -@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); - const scope_manager_1 = require("@typescript-eslint/scope-manager"); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - var AllowedType; - (function (AllowedType) { -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-redundant-type-constituents.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-redundant-type-constituents.js -index 8e14210..6909b15 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-redundant-type-constituents.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-redundant-type-constituents.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const literalToPrimitiveTypeFlags = { - [ts.TypeFlags.BigIntLiteral]: ts.TypeFlags.BigInt, -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-boolean-literal-compare.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-boolean-literal-compare.js -index f2db70a..0de1a41 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-boolean-literal-compare.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-boolean-literal-compare.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'no-unnecessary-boolean-literal-compare', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-condition.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-condition.js -index f2c1db3..7706a54 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-condition.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-condition.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const assertionFunctionUtils_1 = require("../util/assertionFunctionUtils"); - // #region -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-qualifier.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-qualifier.js -index bd0d2fb..c499504 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-qualifier.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-qualifier.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'no-unnecessary-qualifier', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-template-expression.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-template-expression.js -index bd7a477..7612731 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-template-expression.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/no-unnecessary-template-expression.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const rangeToLoc_1 = require("../util/rangeToLoc"); - const evenNumOfBackslashesRegExp = /(? (0, ts_api_utils_1.unionConstituents)(parserServices.getTypeAtLocation(node)).some(t => (0, type_utils_1.isTypeFlagSet)(t, ts.TypeFlags.Null | ts.TypeFlags.Undefined)))) { -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-optional-chain-utils/gatherLogicalOperands.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-optional-chain-utils/gatherLogicalOperands.js -index b357185..3403d8e 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-optional-chain-utils/gatherLogicalOperands.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-optional-chain-utils/gatherLogicalOperands.js -@@ -37,7 +37,7 @@ exports.ComparisonType = exports.NullishComparisonType = exports.OperandValidity - exports.gatherLogicalOperands = gatherLogicalOperands; - const utils_1 = require("@typescript-eslint/utils"); - const ts_api_utils_1 = require("ts-api-utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../../util"); - var Yoda; - (function (Yoda) { -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-readonly.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-readonly.js -index 91e6263..86f9b45 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-readonly.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-readonly.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const getMemberHeadLoc_1 = require("../util/getMemberHeadLoc"); - const functionScopeBoundaries = [ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-return-this-type.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-return-this-type.js -index 4344adf..c9fb363 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-return-this-type.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/prefer-return-this-type.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const ts_api_utils_1 = require("ts-api-utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'prefer-return-this-type', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/promise-function-async.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/promise-function-async.js -index b7418ad..e1e0d14 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/promise-function-async.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/promise-function-async.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'promise-function-async', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-plus-operands.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-plus-operands.js -index 8b05a73..5237409 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-plus-operands.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-plus-operands.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'restrict-plus-operands', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-template-expressions.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-template-expressions.js -index 95d3e2b..4e14c1b 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-template-expressions.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/restrict-template-expressions.js -@@ -2,7 +2,7 @@ - Object.defineProperty(exports, "__esModule", { value: true }); - const type_utils_1 = require("@typescript-eslint/type-utils"); - const utils_1 = require("@typescript-eslint/utils"); --const typescript_1 = require("typescript"); -+const typescript_1 = require("@typescript/typescript6"); - const util_1 = require("../util"); - const testTypeFlag = (flagsToCheck) => type => (0, util_1.isTypeFlagSet)(type, flagsToCheck); - const optionTesters = [ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/return-await.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/return-await.js -index 954a50d..e9f9997 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/return-await.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/return-await.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - exports.default = (0, util_1.createRule)({ - name: 'return-await', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-boolean-expressions.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-boolean-expressions.js -index 1407465..485c7ad 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-boolean-expressions.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-boolean-expressions.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const assertionFunctionUtils_1 = require("../util/assertionFunctionUtils"); - exports.default = (0, util_1.createRule)({ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-void-return.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-void-return.js -index bcbdcf6..f8b0f74 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-void-return.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/strict-void-return.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util = __importStar(require("../util")); - exports.default = util.createRule({ - name: 'strict-void-return', -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/switch-exhaustiveness-check.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/switch-exhaustiveness-check.js -index cce57df..5a4ffc6 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/switch-exhaustiveness-check.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/switch-exhaustiveness-check.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - const DEFAULT_COMMENT_PATTERN = /^no default$/iu; - exports.default = (0, util_1.createRule)({ -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js -index f8e7b6c..82a719d 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/rules/unbound-method.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const util_1 = require("../util"); - /** - * Static methods on these globals are either not `this`-aware or supported being -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/assertionFunctionUtils.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/assertionFunctionUtils.js -index fca21d0..ded477f 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/assertionFunctionUtils.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/assertionFunctionUtils.js -@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); - exports.findTruthinessAssertedArgument = findTruthinessAssertedArgument; - exports.findTypeGuardAssertedArgument = findTypeGuardAssertedArgument; - const utils_1 = require("@typescript-eslint/utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - /** - * Inspect a call expression to see if it's a call to an assertion function. - * If it is, return the node of the argument that is asserted. -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/astUtils.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/astUtils.js -index 6104aef..a2849f2 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/astUtils.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/astUtils.js -@@ -40,7 +40,7 @@ exports.getNameLocationInGlobalDirectiveComment = getNameLocationInGlobalDirecti - exports.forEachReturnStatement = forEachReturnStatement; - exports.forEachChildESTree = forEachChildESTree; - const visitor_keys_1 = require("@typescript-eslint/visitor-keys"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const escapeRegExp_1 = require("./escapeRegExp"); - // deeply re-export, for convenience - __exportStar(require("@typescript-eslint/utils/ast-utils"), exports); -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/baseTypeUtils.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/baseTypeUtils.js -index 448eeb9..e79ec4c 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/baseTypeUtils.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/baseTypeUtils.js -@@ -37,7 +37,7 @@ exports.hasBaseTypes = hasBaseTypes; - exports.matchesTypeOrBaseType = matchesTypeOrBaseType; - const ts_api_utils_1 = require("ts-api-utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - function getBaseTypesForType(checker, type) { - if (!tsutils.isObjectType(type)) { - return []; -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/getOperatorPrecedence.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/getOperatorPrecedence.js -index 5367109..8cbf140 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/getOperatorPrecedence.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/getOperatorPrecedence.js -@@ -5,7 +5,7 @@ exports.getOperatorPrecedenceForNode = getOperatorPrecedenceForNode; - exports.getOperatorPrecedence = getOperatorPrecedence; - exports.getBinaryOperatorPrecedence = getBinaryOperatorPrecedence; - const utils_1 = require("@typescript-eslint/utils"); --const typescript_1 = require("typescript"); -+const typescript_1 = require("@typescript/typescript6"); - var OperatorPrecedence; - (function (OperatorPrecedence) { - // Expression: -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/isHigherPrecedenceThanAwait.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/isHigherPrecedenceThanAwait.js -index a2e7dc4..3248a85 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/isHigherPrecedenceThanAwait.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/isHigherPrecedenceThanAwait.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.isHigherPrecedenceThanAwait = isHigherPrecedenceThanAwait; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const getOperatorPrecedence_1 = require("./getOperatorPrecedence"); - function isHigherPrecedenceThanAwait(tsNode) { - const operator = ts.isBinaryExpression(tsNode) -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/misc.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/misc.js -index 3de4549..20e43c3 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/misc.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/misc.js -@@ -50,7 +50,7 @@ exports.isParenlessArrowFunction = isParenlessArrowFunction; - exports.getStaticMemberAccessValue = getStaticMemberAccessValue; - const type_utils_1 = require("@typescript-eslint/type-utils"); - const utils_1 = require("@typescript-eslint/utils"); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const astUtils_1 = require("./astUtils"); - const DEFINITION_EXTENSIONS = [ - ts.Extension.Dts, -diff --git a/node_modules/@typescript-eslint/eslint-plugin/dist/util/truthinessUtils.js b/node_modules/@typescript-eslint/eslint-plugin/dist/util/truthinessUtils.js -index 2ef0dc4..8c3e713 100644 ---- a/node_modules/@typescript-eslint/eslint-plugin/dist/util/truthinessUtils.js -+++ b/node_modules/@typescript-eslint/eslint-plugin/dist/util/truthinessUtils.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.isPossiblyTruthy = exports.isPossiblyFalsy = void 0; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const getValueOfLiteralType_1 = require("./getValueOfLiteralType"); - // Truthiness utilities - const isTruthyLiteral = (type) => tsutils.isTrueLiteralType(type) || diff --git a/patches/@typescript-eslint/eslint-plugin/details.md b/patches/@typescript-eslint/eslint-plugin/details.md deleted file mode 100644 index dc555a28e5f1..000000000000 --- a/patches/@typescript-eslint/eslint-plugin/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `@typescript-eslint/eslint-plugin` patches - -### [@typescript-eslint+eslint-plugin+8.58.1+001+require-typescript6.patch](@typescript-eslint+eslint-plugin+8.58.1+001+require-typescript6.patch) - -- Reason: - - ``` - typescript-eslint still needs the TypeScript 6 compiler API (`require('typescript')`). - The repo's root `typescript` is TypeScript 7 so that `tsc` is the native compiler. - Point every require at `@typescript/typescript6` so type-aware lint keeps working. - ``` - -- Upstream PR/issue: https://github.com/typescript-eslint/typescript-eslint/issues/10940 -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/@typescript-eslint/parser/@typescript-eslint+parser+8.58.1+001+require-typescript6.patch b/patches/@typescript-eslint/parser/@typescript-eslint+parser+8.58.1+001+require-typescript6.patch deleted file mode 100644 index 5e695bc2453f..000000000000 --- a/patches/@typescript-eslint/parser/@typescript-eslint+parser+8.58.1+001+require-typescript6.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@typescript-eslint/parser/dist/parser.js b/node_modules/@typescript-eslint/parser/dist/parser.js -index c13ab5f..924f954 100644 ---- a/node_modules/@typescript-eslint/parser/dist/parser.js -+++ b/node_modules/@typescript-eslint/parser/dist/parser.js -@@ -9,7 +9,7 @@ const scope_manager_1 = require("@typescript-eslint/scope-manager"); - const typescript_estree_1 = require("@typescript-eslint/typescript-estree"); - const visitor_keys_1 = require("@typescript-eslint/visitor-keys"); - const debug_1 = __importDefault(require("debug")); --const typescript_1 = require("typescript"); -+const typescript_1 = require("@typescript/typescript6"); - const log = (0, debug_1.default)('typescript-eslint:parser:parser'); - function validateBoolean(value, fallback = false) { - if (typeof value !== 'boolean') { diff --git a/patches/@typescript-eslint/parser/details.md b/patches/@typescript-eslint/parser/details.md deleted file mode 100644 index fc69bcd7a042..000000000000 --- a/patches/@typescript-eslint/parser/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `@typescript-eslint/parser` patches - -### [@typescript-eslint+parser+8.58.1+001+require-typescript6.patch](@typescript-eslint+parser+8.58.1+001+require-typescript6.patch) - -- Reason: - - ``` - typescript-eslint still needs the TypeScript 6 compiler API (`require('typescript')`). - The repo's root `typescript` is TypeScript 7 so that `tsc` is the native compiler. - Point every require at `@typescript/typescript6` so type-aware lint keeps working. - ``` - -- Upstream PR/issue: https://github.com/typescript-eslint/typescript-eslint/issues/10940 -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/@typescript-eslint/project-service/@typescript-eslint+project-service+8.61.0+001+require-typescript6.patch b/patches/@typescript-eslint/project-service/@typescript-eslint+project-service+8.61.0+001+require-typescript6.patch deleted file mode 100644 index f8f3f840efd4..000000000000 --- a/patches/@typescript-eslint/project-service/@typescript-eslint+project-service+8.61.0+001+require-typescript6.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/node_modules/@typescript-eslint/project-service/dist/createProjectService.js b/node_modules/@typescript-eslint/project-service/dist/createProjectService.js -index 3f569a8..df248b0 100644 ---- a/node_modules/@typescript-eslint/project-service/dist/createProjectService.js -+++ b/node_modules/@typescript-eslint/project-service/dist/createProjectService.js -@@ -39,7 +39,7 @@ function createProjectService({ host, jsDocParsingMode, options: optionsRaw = {} - // We import this lazily to avoid its cost for users who don't use the service - // TODO: Once we drop support for TS<5.3 we can import from "typescript" directly - // eslint-disable-next-line @typescript-eslint/no-require-imports -- const tsserver = require('typescript/lib/tsserverlibrary'); -+ const tsserver = require('@typescript/old/lib/tsserverlibrary'); - // TODO: see getWatchProgramsForProjects - // We don't watch the disk, we just refer to these when ESLint calls us - // there's a whole separate update pass in maybeInvalidateProgram at the bottom of getWatchProgramsForProjects diff --git a/patches/@typescript-eslint/project-service/details.md b/patches/@typescript-eslint/project-service/details.md deleted file mode 100644 index c5c8abc016d9..000000000000 --- a/patches/@typescript-eslint/project-service/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `@typescript-eslint/project-service` patches - -### [@typescript-eslint+project-service+8.61.0+001+require-typescript6.patch](@typescript-eslint+project-service+8.61.0+001+require-typescript6.patch) - -- Reason: - - ``` - typescript-eslint's project service lazily requires `typescript/lib/tsserverlibrary`. - TypeScript 7 no longer exports that subpath, so type-aware lint fails on `.mts` files. - Point the require at `@typescript/old/lib/tsserverlibrary` (TypeScript 6) instead. - ``` - -- Upstream PR/issue: https://github.com/typescript-eslint/typescript-eslint/issues/10940 -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/@typescript-eslint/type-utils/@typescript-eslint+type-utils+8.58.1+001+require-typescript6.patch b/patches/@typescript-eslint/type-utils/@typescript-eslint+type-utils+8.58.1+001+require-typescript6.patch deleted file mode 100644 index 17849d04e139..000000000000 --- a/patches/@typescript-eslint/type-utils/@typescript-eslint+type-utils+8.58.1+001+require-typescript6.patch +++ /dev/null @@ -1,143 +0,0 @@ -diff --git a/node_modules/@typescript-eslint/type-utils/dist/builtinSymbolLikes.js b/node_modules/@typescript-eslint/type-utils/dist/builtinSymbolLikes.js -index 75bbc56..014ceb7 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/builtinSymbolLikes.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/builtinSymbolLikes.js -@@ -42,7 +42,7 @@ exports.isBuiltinTypeAliasLike = isBuiltinTypeAliasLike; - exports.isBuiltinSymbolLike = isBuiltinSymbolLike; - exports.isBuiltinSymbolLikeRecurser = isBuiltinSymbolLikeRecurser; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const isSymbolFromDefaultLibrary_1 = require("./isSymbolFromDefaultLibrary"); - /** - * @example -diff --git a/node_modules/@typescript-eslint/type-utils/dist/containsAllTypesByName.js b/node_modules/@typescript-eslint/type-utils/dist/containsAllTypesByName.js -index 3307970..5b07eb4 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/containsAllTypesByName.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/containsAllTypesByName.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.containsAllTypesByName = containsAllTypesByName; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const typeFlagUtils_1 = require("./typeFlagUtils"); - /** - * @param type Type being checked by name. -diff --git a/node_modules/@typescript-eslint/type-utils/dist/getContextualType.js b/node_modules/@typescript-eslint/type-utils/dist/getContextualType.js -index b67ef76..53ffd76 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/getContextualType.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/getContextualType.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getContextualType = getContextualType; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - /** - * Returns the contextual type of a given node. - * Contextual type is the type of the target the node is going into. -diff --git a/node_modules/@typescript-eslint/type-utils/dist/getSourceFileOfNode.js b/node_modules/@typescript-eslint/type-utils/dist/getSourceFileOfNode.js -index 3842a6b..ff90a66 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/getSourceFileOfNode.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/getSourceFileOfNode.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getSourceFileOfNode = getSourceFileOfNode; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - /** - * @deprecated - * Gets the source file for a given node -diff --git a/node_modules/@typescript-eslint/type-utils/dist/getTypeName.js b/node_modules/@typescript-eslint/type-utils/dist/getTypeName.js -index 7803902..c7ffd9b 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/getTypeName.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/getTypeName.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getTypeName = getTypeName; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - /** - * Get the type name of a given type. - * @param typeChecker The context sensitive TypeScript TypeChecker. -diff --git a/node_modules/@typescript-eslint/type-utils/dist/isTypeBrandedLiteralLike.js b/node_modules/@typescript-eslint/type-utils/dist/isTypeBrandedLiteralLike.js -index 8368cb1..e827f18 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/isTypeBrandedLiteralLike.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/isTypeBrandedLiteralLike.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.isTypeBrandedLiteralLike = isTypeBrandedLiteralLike; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - function isLiteralOrTaggablePrimitiveLike(type) { - return (type.isLiteral() || - tsutils.isTypeFlagSet(type, ts.TypeFlags.BigInt | -diff --git a/node_modules/@typescript-eslint/type-utils/dist/isTypeReadonly.js b/node_modules/@typescript-eslint/type-utils/dist/isTypeReadonly.js -index 1b348a9..436e7b6 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/isTypeReadonly.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/isTypeReadonly.js -@@ -37,7 +37,7 @@ exports.readonlynessOptionsDefaults = exports.readonlynessOptionsSchema = void 0 - exports.isTypeReadonly = isTypeReadonly; - const utils_1 = require("@typescript-eslint/utils"); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const propertyTypes_1 = require("./propertyTypes"); - const TypeOrValueSpecifier_1 = require("./TypeOrValueSpecifier"); - var Readonlyness; -diff --git a/node_modules/@typescript-eslint/type-utils/dist/predicates.js b/node_modules/@typescript-eslint/type-utils/dist/predicates.js -index d76294b..5a0e358 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/predicates.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/predicates.js -@@ -49,7 +49,7 @@ exports.isTypeBigIntLiteralType = isTypeBigIntLiteralType; - exports.isTypeTemplateLiteralType = isTypeTemplateLiteralType; - const debug_1 = __importDefault(require("debug")); - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const typeFlagUtils_1 = require("./typeFlagUtils"); - const log = (0, debug_1.default)('typescript-eslint:type-utils:predicates'); - /** -diff --git a/node_modules/@typescript-eslint/type-utils/dist/requiresQuoting.js b/node_modules/@typescript-eslint/type-utils/dist/requiresQuoting.js -index 1f2b379..686e249 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/requiresQuoting.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/requiresQuoting.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.requiresQuoting = requiresQuoting; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - /*** Indicates whether identifiers require the use of quotation marks when accessing property definitions and dot notation. */ - function requiresQuoting(name, target = ts.ScriptTarget.ESNext) { - if (name.length === 0) { -diff --git a/node_modules/@typescript-eslint/type-utils/dist/typeFlagUtils.js b/node_modules/@typescript-eslint/type-utils/dist/typeFlagUtils.js -index 6310209..09ad541 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/typeFlagUtils.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/typeFlagUtils.js -@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); - exports.getTypeFlags = getTypeFlags; - exports.isTypeFlagSet = isTypeFlagSet; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const ANY_OR_UNKNOWN = ts.TypeFlags.Any | ts.TypeFlags.Unknown; - /** - * Gets all of the type flags in a type, iterating through unions automatically. -diff --git a/node_modules/@typescript-eslint/type-utils/dist/typeOrValueSpecifiers/typeDeclaredInPackageDeclarationFile.js b/node_modules/@typescript-eslint/type-utils/dist/typeOrValueSpecifiers/typeDeclaredInPackageDeclarationFile.js -index f51b2a1..2224630 100644 ---- a/node_modules/@typescript-eslint/type-utils/dist/typeOrValueSpecifiers/typeDeclaredInPackageDeclarationFile.js -+++ b/node_modules/@typescript-eslint/type-utils/dist/typeOrValueSpecifiers/typeDeclaredInPackageDeclarationFile.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.typeDeclaredInPackageDeclarationFile = typeDeclaredInPackageDeclarationFile; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - function findParentModuleDeclaration(node) { - switch (node.kind) { - case ts.SyntaxKind.ModuleDeclaration: diff --git a/patches/@typescript-eslint/type-utils/details.md b/patches/@typescript-eslint/type-utils/details.md deleted file mode 100644 index 579682ff4604..000000000000 --- a/patches/@typescript-eslint/type-utils/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `@typescript-eslint/type-utils` patches - -### [@typescript-eslint+type-utils+8.58.1+001+require-typescript6.patch](@typescript-eslint+type-utils+8.58.1+001+require-typescript6.patch) - -- Reason: - - ``` - typescript-eslint still needs the TypeScript 6 compiler API (`require('typescript')`). - The repo's root `typescript` is TypeScript 7 so that `tsc` is the native compiler. - Point every require at `@typescript/typescript6` so type-aware lint keeps working. - ``` - -- Upstream PR/issue: https://github.com/typescript-eslint/typescript-eslint/issues/10940 -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/@typescript-eslint/typescript-estree/@typescript-eslint+typescript-estree+8.61.0+001+require-typescript6.patch b/patches/@typescript-eslint/typescript-estree/@typescript-eslint+typescript-estree+8.61.0+001+require-typescript6.patch deleted file mode 100644 index e368d8887726..000000000000 --- a/patches/@typescript-eslint/typescript-estree/@typescript-eslint+typescript-estree+8.61.0+001+require-typescript6.patch +++ /dev/null @@ -1,234 +0,0 @@ -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/check-modifiers.js b/node_modules/@typescript-eslint/typescript-estree/dist/check-modifiers.js -index 33ec104..b90b905 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/check-modifiers.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/check-modifiers.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.checkModifiers = checkModifiers; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const getModifiers_1 = require("./getModifiers"); - const node_utils_1 = require("./node-utils"); - const SyntaxKind = ts.SyntaxKind; -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/check-syntax-errors.js b/node_modules/@typescript-eslint/typescript-estree/dist/check-syntax-errors.js -index 9d40bd0..be91346 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/check-syntax-errors.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/check-syntax-errors.js -@@ -34,7 +34,7 @@ var __importStar = (this && this.__importStar) || (function () { - })(); - Object.defineProperty(exports, "__esModule", { value: true }); - exports.checkSyntaxError = checkSyntaxError; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const check_modifiers_1 = require("./check-modifiers"); - const node_utils_1 = require("./node-utils"); - const SyntaxKind = ts.SyntaxKind; -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/convert-comments.js b/node_modules/@typescript-eslint/typescript-estree/dist/convert-comments.js -index 16ce827..e7cc60c 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/convert-comments.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/convert-comments.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.convertComments = convertComments; - const tsutils = __importStar(require("ts-api-utils")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const node_utils_1 = require("./node-utils"); - const ts_estree_1 = require("./ts-estree"); - /** -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/convert.js b/node_modules/@typescript-eslint/typescript-estree/dist/convert.js -index 3e7dc72..70d8fe3 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/convert.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/convert.js -@@ -37,7 +37,7 @@ exports.Converter = void 0; - exports.convertError = convertError; - // There's lots of funny stuff due to the typing of ts.Node - /* eslint-disable @typescript-eslint/no-non-null-assertion, @typescript-eslint/no-unnecessary-condition, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access */ --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const check_syntax_errors_1 = require("./check-syntax-errors"); - const getModifiers_1 = require("./getModifiers"); - const node_utils_1 = require("./node-utils"); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js -index 85fb0b0..c2a22c7 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createIsolatedProgram.js -@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.createIsolatedProgram = createIsolatedProgram; - const debug_1 = __importDefault(require("debug")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const getScriptKind_1 = require("./getScriptKind"); - const shared_1 = require("./shared"); - const log = (0, debug_1.default)('typescript-eslint:typescript-estree:create-program:createIsolatedProgram'); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createSourceFile.js b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createSourceFile.js -index 2ec8646..e3e1230 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createSourceFile.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/createSourceFile.js -@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); - exports.createSourceFile = createSourceFile; - exports.createNoProgram = createNoProgram; - const debug_1 = __importDefault(require("debug")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const source_files_1 = require("../source-files"); - const getScriptKind_1 = require("./getScriptKind"); - const log = (0, debug_1.default)('typescript-eslint:typescript-estree:create-program:createSourceFile'); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getScriptKind.js b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getScriptKind.js -index 6c948b5..c9fc142 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getScriptKind.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getScriptKind.js -@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); - exports.getScriptKind = getScriptKind; - exports.getLanguageVariant = getLanguageVariant; - const node_path_1 = __importDefault(require("node:path")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - function getScriptKind(filePath, jsx) { - const extension = node_path_1.default.extname(filePath).toLowerCase(); - // note - we only respect the user's jsx setting for unknown extensions -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getWatchProgramsForProjects.js b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getWatchProgramsForProjects.js -index 1979990..9a42011 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getWatchProgramsForProjects.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/getWatchProgramsForProjects.js -@@ -40,7 +40,7 @@ exports.clearWatchCaches = clearWatchCaches; - exports.getWatchProgramsForProjects = getWatchProgramsForProjects; - const debug_1 = __importDefault(require("debug")); - const node_fs_1 = __importDefault(require("node:fs")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const source_files_1 = require("../source-files"); - const shared_1 = require("./shared"); - const log = (0, debug_1.default)('typescript-eslint:typescript-estree:create-program:getWatchProgramsForProjects'); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/shared.js b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/shared.js -index cbbd59c..4552d4a 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/shared.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/shared.js -@@ -45,7 +45,7 @@ exports.getAstFromProgram = getAstFromProgram; - exports.createHash = createHash; - const tsconfig_utils_1 = require("@typescript-eslint/tsconfig-utils"); - const node_path_1 = __importDefault(require("node:path")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - /** - * Default compiler options for program generation - */ -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/useProvidedPrograms.js b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/useProvidedPrograms.js -index 7369ed9..979deb6 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/create-program/useProvidedPrograms.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/create-program/useProvidedPrograms.js -@@ -41,7 +41,7 @@ exports.createProgramFromConfigFile = createProgramFromConfigFile; - const tsconfig_utils_1 = require("@typescript-eslint/tsconfig-utils"); - const debug_1 = __importDefault(require("debug")); - const path = __importStar(require("node:path")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const shared_1 = require("./shared"); - const log = (0, debug_1.default)('typescript-eslint:typescript-estree:create-program:useProvidedPrograms'); - function useProvidedPrograms(programInstances, parseSettings) { -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/getModifiers.js b/node_modules/@typescript-eslint/typescript-estree/dist/getModifiers.js -index 4c16405..ca0f2da 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/getModifiers.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/getModifiers.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getModifiers = getModifiers; - exports.getDecorators = getDecorators; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const version_check_1 = require("./version-check"); - const isAtLeast48 = version_check_1.typescriptVersionIsAtLeast['4.8']; - function getModifiers(node, includeIllegalModifiers = false) { -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/node-utils.js b/node_modules/@typescript-eslint/typescript-estree/dist/node-utils.js -index 6833020..3f25936 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/node-utils.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/node-utils.js -@@ -71,7 +71,7 @@ exports.isValidAssignmentTarget = isValidAssignmentTarget; - exports.getNamespaceModifiers = getNamespaceModifiers; - exports.declarationNameToString = declarationNameToString; - exports.isEntityNameExpression = isEntityNameExpression; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const getModifiers_1 = require("./getModifiers"); - const xhtml_entities_1 = require("./jsx/xhtml-entities"); - const ts_estree_1 = require("./ts-estree"); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/createParseSettings.js b/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/createParseSettings.js -index 0109180..987ea36 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/createParseSettings.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/createParseSettings.js -@@ -42,7 +42,7 @@ exports.clearTSServerProjectService = clearTSServerProjectService; - const project_service_1 = require("@typescript-eslint/project-service"); - const debug_1 = __importDefault(require("debug")); - const node_path_1 = __importDefault(require("node:path")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const shared_1 = require("../create-program/shared"); - const validateDefaultProjectForFilesGlob_1 = require("../create-program/validateDefaultProjectForFilesGlob"); - const source_files_1 = require("../source-files"); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/warnAboutTSVersion.js b/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/warnAboutTSVersion.js -index 7a133e8..7e3a827 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/warnAboutTSVersion.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/parseSettings/warnAboutTSVersion.js -@@ -39,7 +39,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); - exports.SUPPORTED_TYPESCRIPT_VERSIONS = void 0; - exports.warnAboutTSVersion = warnAboutTSVersion; - const semver_1 = __importDefault(require("semver")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const version_1 = require("../version"); - /** - * This needs to be kept in sync with package.json in the typescript-eslint monorepo -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/semantic-or-syntactic-errors.js b/node_modules/@typescript-eslint/typescript-estree/dist/semantic-or-syntactic-errors.js -index 5e77d97..241dd97 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/semantic-or-syntactic-errors.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/semantic-or-syntactic-errors.js -@@ -1,7 +1,7 @@ - "use strict"; - Object.defineProperty(exports, "__esModule", { value: true }); - exports.getFirstSemanticOrSyntacticError = getFirstSemanticOrSyntacticError; --const typescript_1 = require("typescript"); -+const typescript_1 = require("@typescript/typescript6"); - /** - * By default, diagnostics from the TypeScript compiler contain all errors - regardless of whether - * they are related to generic ECMAScript standards, or TypeScript-specific constructs. -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/source-files.js b/node_modules/@typescript-eslint/typescript-estree/dist/source-files.js -index 51151c0..f0787ea 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/source-files.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/source-files.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.isSourceFile = isSourceFile; - exports.getCodeText = getCodeText; --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - function isSourceFile(code) { - if (typeof code !== 'object' || code == null) { - return false; -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/useProgramFromProjectService.js b/node_modules/@typescript-eslint/typescript-estree/dist/useProgramFromProjectService.js -index 20f3d7b..a5fde52 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/useProgramFromProjectService.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/useProgramFromProjectService.js -@@ -41,7 +41,7 @@ const debug_1 = __importDefault(require("debug")); - const minimatch_1 = require("minimatch"); - const node_path_1 = __importDefault(require("node:path")); - const node_util_1 = __importDefault(require("node:util")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - const createProjectProgram_1 = require("./create-program/createProjectProgram"); - const createSourceFile_1 = require("./create-program/createSourceFile"); - const shared_1 = require("./create-program/shared"); -diff --git a/node_modules/@typescript-eslint/typescript-estree/dist/version-check.js b/node_modules/@typescript-eslint/typescript-estree/dist/version-check.js -index 54d545d..19fcc22 100644 ---- a/node_modules/@typescript-eslint/typescript-estree/dist/version-check.js -+++ b/node_modules/@typescript-eslint/typescript-estree/dist/version-check.js -@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () { - Object.defineProperty(exports, "__esModule", { value: true }); - exports.typescriptVersionIsAtLeast = void 0; - const semver = __importStar(require("semver")); --const ts = __importStar(require("typescript")); -+const ts = __importStar(require("@typescript/typescript6")); - function semverCheck(version) { - return semver.satisfies(ts.version, `>= ${version}.0 || >= ${version}.1-rc || >= ${version}.0-beta`, { - includePrerelease: true, diff --git a/patches/@typescript-eslint/typescript-estree/details.md b/patches/@typescript-eslint/typescript-estree/details.md deleted file mode 100644 index a1e3c03ae926..000000000000 --- a/patches/@typescript-eslint/typescript-estree/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `@typescript-eslint/typescript-estree` patches - -### [@typescript-eslint+typescript-estree+8.61.0+001+require-typescript6.patch](@typescript-eslint+typescript-estree+8.61.0+001+require-typescript6.patch) - -- Reason: - - ``` - typescript-eslint still needs the TypeScript 6 compiler API (`require('typescript')`). - The repo's root `typescript` is TypeScript 7 so that `tsc` is the native compiler. - Point every require at `@typescript/typescript6` so type-aware lint keeps working. - ``` - -- Upstream PR/issue: https://github.com/typescript-eslint/typescript-eslint/issues/10940 -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/eslint-config-expensify/details.md b/patches/eslint-config-expensify/details.md deleted file mode 100644 index a69d03ec6265..000000000000 --- a/patches/eslint-config-expensify/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `eslint-config-expensify` patches - -### [eslint-config-expensify+4.0.8+001+import-typescript6.patch](eslint-config-expensify+4.0.8+001+import-typescript6.patch) - -- Reason: - - ``` - Type-aware Expensify rules import the classic compiler API from `typescript`. - The repo's root `typescript` is TypeScript 7 so that `tsc` is the native compiler. - Point the import at `@typescript/typescript6` so those rules keep working. - ``` - -- Upstream PR/issue: N/A -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/eslint-config-expensify/eslint-config-expensify+4.0.8+001+import-typescript6.patch b/patches/eslint-config-expensify/eslint-config-expensify+4.0.8+001+import-typescript6.patch deleted file mode 100644 index 1886344c56e1..000000000000 --- a/patches/eslint-config-expensify/eslint-config-expensify+4.0.8+001+import-typescript6.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/node_modules/eslint-config-expensify/eslint-plugin-expensify/utils/typeUtil.js b/node_modules/eslint-config-expensify/eslint-plugin-expensify/utils/typeUtil.js -index ef2c36f..b56e340 100644 ---- a/node_modules/eslint-config-expensify/eslint-plugin-expensify/utils/typeUtil.js -+++ b/node_modules/eslint-config-expensify/eslint-plugin-expensify/utils/typeUtil.js -@@ -1,6 +1,6 @@ - /* eslint-disable no-bitwise */ - import _ from 'underscore'; --import ts from 'typescript'; -+import ts from '@typescript/typescript6'; - - function isBoolean(type) { - if ((type.flags & (ts.TypeFlags.Boolean | ts.TypeFlags.BooleanLike | ts.TypeFlags.BooleanLiteral)) !== 0) { diff --git a/patches/ts-api-utils/details.md b/patches/ts-api-utils/details.md deleted file mode 100644 index 8bbd8c0eafd3..000000000000 --- a/patches/ts-api-utils/details.md +++ /dev/null @@ -1,15 +0,0 @@ -# `ts-api-utils` patches - -### [ts-api-utils+2.5.0+001+require-typescript6.patch](ts-api-utils+2.5.0+001+require-typescript6.patch) - -- Reason: - - ``` - typescript-eslint pulls in ts-api-utils, which imports the classic compiler API from `typescript`. - The repo's root `typescript` is TypeScript 7 so that `tsc` is the native compiler. - Point the CJS require and ESM import at `@typescript/typescript6` so type-aware lint keeps working. - ``` - -- Upstream PR/issue: https://github.com/typescript-eslint/typescript-eslint/issues/10940 -- E/App issue: https://github.com/Expensify/App/issues/99288 -- PR introducing patch: https://github.com/Expensify/App/pull/99017 diff --git a/patches/ts-api-utils/ts-api-utils+2.5.0+001+require-typescript6.patch b/patches/ts-api-utils/ts-api-utils+2.5.0+001+require-typescript6.patch deleted file mode 100644 index f9b3e67e5387..000000000000 --- a/patches/ts-api-utils/ts-api-utils+2.5.0+001+require-typescript6.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/node_modules/ts-api-utils/lib/index.cjs b/node_modules/ts-api-utils/lib/index.cjs -index 4208526..807cb5d 100644 ---- a/node_modules/ts-api-utils/lib/index.cjs -+++ b/node_modules/ts-api-utils/lib/index.cjs -@@ -1,6 +1,6 @@ - 'use strict'; - --var ts9 = require('typescript'); -+var ts9 = require('@typescript/typescript6'); - - function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } - -diff --git a/node_modules/ts-api-utils/lib/index.js b/node_modules/ts-api-utils/lib/index.js -index 5902556..dbd2a86 100644 ---- a/node_modules/ts-api-utils/lib/index.js -+++ b/node_modules/ts-api-utils/lib/index.js -@@ -1,4 +1,4 @@ --import ts9 from 'typescript'; -+import ts9 from '@typescript/typescript6'; - - // src/comments.ts - function forEachToken(node, callback, sourceFile = node.getSourceFile()) { diff --git a/tests/unit/NoDirectPersonalDetailsListRuleTest.ts b/tests/unit/NoDirectPersonalDetailsListRuleTest.ts index bd7990886e6c..a405e6cccb1c 100644 --- a/tests/unit/NoDirectPersonalDetailsListRuleTest.ts +++ b/tests/unit/NoDirectPersonalDetailsListRuleTest.ts @@ -1,7 +1,8 @@ import type {Rule} from 'eslint'; import {RuleTester} from 'eslint'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/unit/NoLayoutSpacingConditionalRuleTest.ts b/tests/unit/NoLayoutSpacingConditionalRuleTest.ts index ae68cd51267d..e05cd879e3d8 100644 --- a/tests/unit/NoLayoutSpacingConditionalRuleTest.ts +++ b/tests/unit/NoLayoutSpacingConditionalRuleTest.ts @@ -1,7 +1,8 @@ import type {Rule} from 'eslint'; import {RuleTester} from 'eslint'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/unit/NoOnyxGetSnapshotKeyRuleTest.ts b/tests/unit/NoOnyxGetSnapshotKeyRuleTest.ts index aa9d12496ad6..fe9259637048 100644 --- a/tests/unit/NoOnyxGetSnapshotKeyRuleTest.ts +++ b/tests/unit/NoOnyxGetSnapshotKeyRuleTest.ts @@ -9,7 +9,8 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {Rule} from 'eslint'; import {Linter, RuleTester} from 'eslint'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/unit/NoOnyxLoadingStatusRuleTest.ts b/tests/unit/NoOnyxLoadingStatusRuleTest.ts index b574b7210104..b6db02636af3 100644 --- a/tests/unit/NoOnyxLoadingStatusRuleTest.ts +++ b/tests/unit/NoOnyxLoadingStatusRuleTest.ts @@ -1,7 +1,8 @@ import type {Rule} from 'eslint'; import {RuleTester} from 'eslint'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/unit/NoRawTypographyRuleTest.ts b/tests/unit/NoRawTypographyRuleTest.ts index c33543528285..20d314032e5e 100644 --- a/tests/unit/NoRawTypographyRuleTest.ts +++ b/tests/unit/NoRawTypographyRuleTest.ts @@ -1,7 +1,8 @@ import type {Rule} from 'eslint'; import {RuleTester} from 'eslint'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/unit/NoUnsafeOnyxReadRuleTest.ts b/tests/unit/NoUnsafeOnyxReadRuleTest.ts index 367d5b0fa359..06f3899d35e4 100644 --- a/tests/unit/NoUnsafeOnyxReadRuleTest.ts +++ b/tests/unit/NoUnsafeOnyxReadRuleTest.ts @@ -5,7 +5,8 @@ import type {Rule} from 'eslint'; import {RuleTester} from 'eslint'; import path from 'path'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/unit/RequireLocaleForLocalizedDateFormatRuleTest.ts b/tests/unit/RequireLocaleForLocalizedDateFormatRuleTest.ts index 88b26f19544c..15968f9d841b 100644 --- a/tests/unit/RequireLocaleForLocalizedDateFormatRuleTest.ts +++ b/tests/unit/RequireLocaleForLocalizedDateFormatRuleTest.ts @@ -1,7 +1,8 @@ import type {Rule} from 'eslint'; import {RuleTester} from 'eslint'; -import {parser as tsParser} from 'typescript-eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; type LocalRuleModule = Rule.RuleModule & { name: string; diff --git a/tests/utils/typeScriptRuleTestParser.d.ts b/tests/utils/typeScriptRuleTestParser.d.ts new file mode 100644 index 000000000000..9f5dc0cf28d0 --- /dev/null +++ b/tests/utils/typeScriptRuleTestParser.d.ts @@ -0,0 +1,5 @@ +import type {Linter} from 'eslint'; + +declare const typeScriptRuleTestParser: Linter.Parser; + +export = typeScriptRuleTestParser; diff --git a/tests/utils/typeScriptRuleTestParser.js b/tests/utils/typeScriptRuleTestParser.js new file mode 100644 index 000000000000..9ab53c194f33 --- /dev/null +++ b/tests/utils/typeScriptRuleTestParser.js @@ -0,0 +1,86 @@ +/** + * Parses rule fixtures with Oxc's TypeScript ESTree nodes and TypeScript scope analysis, without a + * compiler. Babel supplies the ESLint tokens and comments that Oxc's parser API does not expose. + */ +const babelParser = require('@babel/eslint-parser'); +const {analyze} = require('@typescript-eslint/scope-manager'); + +// Node can load Oxc's ESM entry point; Jest's CommonJS module loader cannot. +const {parseSync, visitorKeys} = process.getBuiltinModule('module').createRequire(__filename)('oxc-parser'); + +/** + * Oxc provides UTF-16 ranges, while ESLint also requires line and column locations on every node. + * @param {Object} node an Oxc ESTree node + * @param {Number[]} lineStarts UTF-16 offsets at the start of each line + * @returns {void} + */ +function addLocations(node, lineStarts) { + function getPosition(offset) { + let low = 0; + let high = lineStarts.length; + while (low + 1 < high) { + const middle = Math.floor((low + high) / 2); + if (lineStarts[middle] <= offset) { + low = middle; + } else { + high = middle; + } + } + return {line: low + 1, column: offset - lineStarts[low]}; + } + + Object.assign(node, {loc: {start: getPosition(node.start), end: getPosition(node.end)}}); + for (const key of visitorKeys[node.type] ?? []) { + const children = Array.isArray(node[key]) ? node[key] : [node[key]]; + for (const child of children) { + if (child) { + addLocations(child, lineStarts); + } + } + } +} + +module.exports = { + meta: {name: 'expensify-rule-test-parser', version: '1.0.0'}, + parseForESLint(code, options) { + const tokenized = babelParser.parse(code, { + ...options, + requireConfigFile: false, + babelOptions: { + babelrc: false, + configFile: false, + parserOpts: {plugins: ['typescript', 'jsx']}, + }, + }); + + const parsed = parseSync(options.filePath ?? 'fixture.tsx', code, { + lang: options.ecmaFeatures?.jsx ? 'tsx' : 'ts', + sourceType: options.sourceType, + range: true, + preserveParens: false, + }); + if (parsed.errors.length > 0) { + throw new SyntaxError(parsed.errors.map((error) => error.message).join('\n')); + } + + const ast = parsed.program; + const lineStarts = [0]; + for (const match of code.matchAll(/\r\n|[\n\r\u2028\u2029]/g)) { + lineStarts.push(match.index + match[0].length); + } + addLocations(ast, lineStarts); + ast.tokens = tokenized.tokens; + ast.comments = tokenized.comments; + + return { + ast, + visitorKeys, + scopeManager: analyze(ast, { + sourceType: options.sourceType, + childVisitorKeys: visitorKeys, + jsxPragma: null, + jsxFragmentName: null, + }), + }; + }, +}; From 0bc469d961261318fcec383741f2bc4672a50462 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 14:05:25 -0800 Subject: [PATCH 08/10] fix(lint): tokenize TypeScript decorators --- tests/unit/NoRawTypographyRuleTest.ts | 7 +++++++ tests/utils/typeScriptRuleTestParser.js | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/unit/NoRawTypographyRuleTest.ts b/tests/unit/NoRawTypographyRuleTest.ts index 20d314032e5e..7bc30eeb66d2 100644 --- a/tests/unit/NoRawTypographyRuleTest.ts +++ b/tests/unit/NoRawTypographyRuleTest.ts @@ -189,6 +189,13 @@ describe('no-raw-typography', () => { tsRuleTester.run(`${ruleModule.name} (TS assertions)`, ruleModule, { valid: ['const style = {fontSize: fontScale.text as number};'], invalid: [ + // Given valid TypeScript combining both decorator forms and a decorated auto-accessor, + // When the rule reads the raw font size after an astral character, + // Then tokenization must succeed and the diagnostic must retain its UTF-16 columns. + { + code: "export @decorate class Model { constructor(@inject value: string) {} @decorate accessor label = '🚀'; @decorate accessor style = {fontSize: 17}; }", + errors: [{messageId: 'rawTypography', line: 1, column: 141, endLine: 1, endColumn: 143}], + }, { code: 'const style = {fontSize: variables.fontSizeNormal as number};', errors: [{messageId: 'rawTypographyVariable'}], diff --git a/tests/utils/typeScriptRuleTestParser.js b/tests/utils/typeScriptRuleTestParser.js index 9ab53c194f33..440134fc9384 100644 --- a/tests/utils/typeScriptRuleTestParser.js +++ b/tests/utils/typeScriptRuleTestParser.js @@ -49,7 +49,12 @@ module.exports = { babelOptions: { babelrc: false, configFile: false, - parserOpts: {plugins: ['typescript', 'jsx']}, + parserOpts: { + plugins: ['typescript', 'jsx', 'decorators', 'decoratorAutoAccessors'], + // Oxc validates syntax. Babel only supplies tokens, so allow its recoverable error + // for TypeScript parameter decorators, which standard JavaScript decorators forbid. + errorRecovery: true, + }, }, }); From 528ac73741c25c3a1e065b686ef18c6ae8d76221 Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 14:11:00 -0800 Subject: [PATCH 09/10] perf: scan comment trivia without token trees --- scripts/utils/TSCompilerUtils.ts | 31 +++++++++++++++++---------- tests/tooling/TSCompilerUtils.test.ts | 29 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/scripts/utils/TSCompilerUtils.ts b/scripts/utils/TSCompilerUtils.ts index b89e9344c731..42ef95cca2f2 100644 --- a/scripts/utils/TSCompilerUtils.ts +++ b/scripts/utils/TSCompilerUtils.ts @@ -537,7 +537,7 @@ function isStringConcatenationChain(node: ts.BinaryExpression): boolean { /** * The native printer preserves comments, but translation targets must not carry English context annotations. - * Inspect parsed token trivia so comment markers inside strings, templates and regular expressions remain intact. + * AST literal boundaries keep the scanner from treating regex or template contents as comments. */ async function printWithoutComments(api: API, node: ts.Node): Promise { // The beta printer cooks tagged templates without substitutions, changing the raw strings passed to their tags. @@ -546,20 +546,29 @@ async function printWithoutComments(api: API, node: ts.Node): Promise { const lease = await api.createSourceFile('printed.ts', code); try { const ranges = new Map(); - const collectComments = (child: ts.Node): void => { - for (const position of [child.pos, child.end]) { - const comments = [...(ts.getLeadingCommentRanges(code, position) ?? []), ...(ts.getTrailingCommentRanges(code, position) ?? [])]; - for (const comment of comments) { - ranges.set(comment.pos, comment.end); - } + const literalEnds = new Map(); + const printedTemplates: ts.NoSubstitutionTemplateLiteral[] = []; + const collectLiteralBoundaries = (child: ts.Node): void => { + if (ts.isRegularExpressionLiteral(child) || ts.isNoSubstitutionTemplateLiteral(child) || ts.isTemplateLiteralLikeNode(child)) { + literalEnds.set(child.getStart(), child.end); } - for (const token of child.getChildren(lease.sourceFile)) { - collectComments(token); + if (ts.isTaggedTemplateExpression(child) && ts.isNoSubstitutionTemplateLiteral(child.template)) { + printedTemplates.push(child.template); } + child.forEachChild(collectLiteralBoundaries); }; - collectComments(lease.sourceFile); + collectLiteralBoundaries(lease.sourceFile); + const scanner = ts.createScanner(false, lease.sourceFile.languageVariant, code); + for (let token = scanner.scan(); token !== ts.SyntaxKind.EndOfFile; token = scanner.scan()) { + const start = scanner.getTokenStart(); + const literalEnd = literalEnds.get(start); + if (literalEnd !== undefined) { + scanner.resetTokenState(literalEnd); + } else if (token === ts.SyntaxKind.SingleLineCommentTrivia || token === ts.SyntaxKind.MultiLineCommentTrivia) { + ranges.set(start, scanner.getTokenEnd()); + } + } const replacements = [...ranges].map(([start, end]) => ({start, end, text: code.slice(start, end).replaceAll(/[^\r\n]/g, ' ')})); - const printedTemplates = getTaggedTemplatesWithoutSubstitutions(lease.sourceFile); if (printedTemplates.length !== rawTemplates.length) { throw new Error('Could not preserve tagged template raw text after printing'); } diff --git a/tests/tooling/TSCompilerUtils.test.ts b/tests/tooling/TSCompilerUtils.test.ts index 1038d7852872..c56a9bcd48c1 100644 --- a/tests/tooling/TSCompilerUtils.test.ts +++ b/tests/tooling/TSCompilerUtils.test.ts @@ -1050,6 +1050,35 @@ describe('TSCompilerUtils', () => { } }); + it('removes empty and ordinary comments without changing regex, URL or nested template values', async () => { + // Given regex and template contents that resemble comments, lexing must distinguish them from actual trivia. + const code = Str.dedent(` + /* real comment */ + (() => { + const url = 'https://example.com/*literal*/'; + const pattern = /[/*][/*]/; + const division = 8 / /**/ 2; // real comment + return [ + pattern.test(url), + \`// literal \${/**/ pattern.test(url) ? \`nested /* literal */ \${division}\` : 'none'} /* literal */\`, + url, + division, + ]; + })() + `); + const expected: unknown = runInNewContext(code); + const sourceFile = await createSourceFile(code); + + // When comments are removed, AST literal boundaries let the scanner resume inside template substitutions. + const printed = await TSCompilerUtils.printWithoutComments(api, sourceFile); + const actual: unknown = runInNewContext(printed); + + // Then evaluation is unchanged, including division, while ordinary and empty comments are absent. + expect(actual).toEqual(expected); + expect(printed).not.toContain('real comment'); + expect(printed).not.toContain('/**/'); + }); + describe('objectHas', () => { it('returns true for top-level property that exists', async () => { const source = await createSourceFile( From e8426f5b81393732a235157f14c1a23b8152b66c Mon Sep 17 00:00:00 2001 From: rory Date: Mon, 9 Nov 2026 14:17:03 -0800 Subject: [PATCH 10/10] fix(lint): preserve parser and Unicode parity --- config/oxlint/vendor/README.md | 3 +- config/oxlint/vendor/build.mjs | 15 +++- config/oxlint/vendor/naming-convention.mjs | 16 ++++- tests/unit/TypeScriptRuleTestParserTest.ts | 80 ++++++++++++++++++++++ tests/utils/typeScriptRuleTestParser.js | 15 +++- 5 files changed, 120 insertions(+), 9 deletions(-) create mode 100644 tests/unit/TypeScriptRuleTestParserTest.ts diff --git a/config/oxlint/vendor/README.md b/config/oxlint/vendor/README.md index f170e096b9fe..26ee3d225dee 100644 --- a/config/oxlint/vendor/README.md +++ b/config/oxlint/vendor/README.md @@ -31,7 +31,8 @@ Florian Weber (`florian@expensify.com`) and Expensify and declares the ISC licen - Expensify's disabled `boolean-conditional-rendering` and `prefer-at` rules are not loaded. The existing `unicorn/prefer-at` rule and `preferLocaleCompareFromContext.mjs` implementation remain configured. -- The naming rule uses Babel's Unicode identifier validation instead of compiler scanner helpers. +- The naming rule uses Babel's identifier predicates instead of compiler scanner helpers, preserving + the rule's UTF-16 code-unit iteration rather than treating surrogate pairs as Unicode code points. App's configuration has no type or unused modifiers. Those selectors fail explicitly if introduced; they are not silently ignored. All other naming checks and diagnostic messages are retained. - Testing Library and Storybook use the same compiler-free AST predicates, scope utilities, rule diff --git a/config/oxlint/vendor/build.mjs b/config/oxlint/vendor/build.mjs index 8594fe513a1d..39e6138b1b8c 100644 --- a/config/oxlint/vendor/build.mjs +++ b/config/oxlint/vendor/build.mjs @@ -45,11 +45,22 @@ export {ASTUtils, AST_NODE_TYPES, AST_TOKEN_TYPES, ESLintUtils, RuleCreator, TSE const namingUtilities = ` import {ESLintUtils} from '@typescript-eslint/utils'; -import {isIdentifierName} from '@babel/helper-validator-identifier'; +import {isIdentifierStart, isIdentifierChar} from '@babel/helper-validator-identifier'; const {RuleCreator} = ESLintUtils; const createRule = RuleCreator((name) => 'https://typescript-eslint.io/rules/' + name); const getEnumNames = (values) => Object.keys(values).filter((key) => !/^\\d+$/.test(key)); -const requiresQuoting = (name) => !isIdentifierName(name); +function requiresQuoting(name) { + // The naming rule checks UTF-16 code units, not Unicode code points, including surrogate pairs. + if (name.length === 0 || !isIdentifierStart(name.charCodeAt(0))) { + return true; + } + for (let index = 1; index < name.length; index += 1) { + if (!isIdentifierChar(name.charCodeAt(index))) { + return true; + } + } + return false; +} function getParserServices(_context, allowWithoutFullTypeInformation) { if (!allowWithoutFullTypeInformation) { throw new Error('Type-based naming selectors require a type-aware rule.'); diff --git a/config/oxlint/vendor/naming-convention.mjs b/config/oxlint/vendor/naming-convention.mjs index aabc6d1e8b5e..375aa28b39f6 100644 --- a/config/oxlint/vendor/naming-convention.mjs +++ b/config/oxlint/vendor/naming-convention.mjs @@ -59,20 +59,30 @@ __export(naming_utilities_exports, { requiresQuoting: () => requiresQuoting }); import { ESLintUtils } from "./syntax-rule-utilities.mjs"; -import { isIdentifierName } from "@babel/helper-validator-identifier"; +import { isIdentifierStart, isIdentifierChar } from "@babel/helper-validator-identifier"; +function requiresQuoting(name) { + if (name.length === 0 || !isIdentifierStart(name.charCodeAt(0))) { + return true; + } + for (let index = 1; index < name.length; index += 1) { + if (!isIdentifierChar(name.charCodeAt(index))) { + return true; + } + } + return false; +} function getParserServices(_context, allowWithoutFullTypeInformation) { if (!allowWithoutFullTypeInformation) { throw new Error("Type-based naming selectors require a type-aware rule."); } return {}; } -var RuleCreator, createRule, getEnumNames, requiresQuoting; +var RuleCreator, createRule, getEnumNames; var init_naming_utilities = __esm({ "syntax-only:naming-utilities"() { ({ RuleCreator } = ESLintUtils); createRule = RuleCreator((name) => "https://typescript-eslint.io/rules/" + name); getEnumNames = (values) => Object.keys(values).filter((key) => !/^\d+$/.test(key)); - requiresQuoting = (name) => !isIdentifierName(name); } }); diff --git a/tests/unit/TypeScriptRuleTestParserTest.ts b/tests/unit/TypeScriptRuleTestParserTest.ts new file mode 100644 index 000000000000..89bfc5469656 --- /dev/null +++ b/tests/unit/TypeScriptRuleTestParserTest.ts @@ -0,0 +1,80 @@ +/** @jest-environment node */ +import type {Rule} from 'eslint'; + +import {Linter} from 'eslint'; + +import tsParser from '../utils/typeScriptRuleTestParser'; + +const nativeRequire = process.getBuiltinModule('module').createRequire(__filename); +const namingModule: unknown = nativeRequire('../../config/oxlint/vendor/naming-convention.mjs'); +const namingRule: unknown = typeof namingModule === 'object' && namingModule !== null ? Reflect.get(namingModule, 'default') : undefined; + +function isRuleModule(rule: unknown): rule is Rule.RuleModule { + return typeof rule === 'object' && rule !== null && typeof Reflect.get(rule, 'create') === 'function'; +} + +if (!isRuleModule(namingRule)) { + throw new TypeError('Expected the syntax-only naming convention rule.'); +} + +describe('TypeScript rule-test parser syntax modes', () => { + it.each([ + {filename: 'fixture.ts', jsx: false, code: 'const x = foo;', valid: true}, + {filename: 'fixture.ts', jsx: true, code: 'const x = (x: T) => x;', valid: true}, + {filename: 'fixture.ts', jsx: true, code: 'const x =
;', valid: false}, + {filename: 'fixture.tsx', jsx: undefined, code: 'const x =
;', valid: true}, + {filename: 'fixture.tsx', jsx: false, code: 'const x =
;', valid: true}, + {filename: 'fixture.tsx', jsx: false, code: 'const x = foo;', valid: false}, + {filename: 'fixture.cts', jsx: true, code: 'const x = foo;', valid: true}, + {filename: 'fixture.mts', jsx: true, code: 'const x = (x: T) => x;', valid: true}, + {filename: 'fixture.js', jsx: false, code: 'const x =
;', valid: true}, + {filename: 'fixture.jsx', jsx: false, code: 'const x =
;', valid: true}, + {filename: 'fixture.unknown', jsx: false, code: 'const x = foo;', valid: true}, + {filename: 'fixture.unknown', jsx: true, code: 'const x =
;', valid: true}, + {filename: 'fixture.ts', jsx: false, code: '@sealed class C {}', valid: true}, + {filename: 'fixture.ts', jsx: false, code: 'class C {accessor foo = 1;}', valid: true}, + ])('preserves the syntax mode of $filename with JSX=$jsx', ({filename, jsx, code, valid}) => { + // Given a known extension that overrides JSX options, or an unknown one that respects them, + // When ESLint uses both the tokenizer and AST parser, + // eslint-disable-next-line @typescript-eslint/naming-convention -- ESLint rule IDs contain punctuation and cannot be renamed. + const messages = new Linter().verify(code, [{files: ['**/*.*'], languageOptions: {parser: tsParser, parserOptions: {ecmaFeatures: {jsx}}}, rules: {'no-debugger': 'error'}}], { + filename, + }); + + // Then valid syntax stays accepted and the opposite language variant stays rejected. + if (valid) { + expect(messages).toEqual([]); + } else { + expect(messages).toHaveLength(1); + expect(messages.at(0)?.fatal).toBe(true); + } + }); +}); + +describe('syntax-only naming convention Unicode parity', () => { + it.each([ + {key: "'𝒜'", errors: ['doesNotMatchFormat']}, + {key: '𝒜', errors: ['doesNotMatchFormat']}, + {key: "'a𝒜'", errors: ['doesNotMatchFormat']}, + {key: 'a𝒜', errors: ['doesNotMatchFormat']}, + {key: "'É'", errors: []}, + {key: 'É', errors: []}, + ])('preserves the property diagnostic for $key', ({key, errors}) => { + // Given App's variable/property naming options and a quoted or unquoted Unicode key, + const options = [ + {selector: ['variable', 'property'], format: null, filter: {regex: '^__esModule$', match: true}}, + {selector: ['variable', 'property'], format: ['camelCase', 'UPPER_CASE', 'PascalCase'], filter: {regex: '^private_[a-z][a-zA-Z0-9]*$', match: false}}, + ]; + + // When the rule checks the original UTF-16 code units rather than combined surrogate pairs, + const messages = new Linter().verify( + `const object = {${key}: 1};`, + // eslint-disable-next-line @typescript-eslint/naming-convention -- ESLint plugin rule IDs require a slash between the plugin and rule names. + [{files: ['**/*.ts'], languageOptions: {parser: tsParser}, plugins: {test: {rules: {naming: namingRule}}}, rules: {'test/naming': ['error', ...options]}}], + {filename: 'fixture.ts'}, + ); + + // Then astral keys retain their existing diagnostic while BMP identifiers remain valid. + expect(messages.map((message) => message.messageId)).toEqual(errors); + }); +}); diff --git a/tests/utils/typeScriptRuleTestParser.js b/tests/utils/typeScriptRuleTestParser.js index 440134fc9384..e1b109a7bf2b 100644 --- a/tests/utils/typeScriptRuleTestParser.js +++ b/tests/utils/typeScriptRuleTestParser.js @@ -2,11 +2,14 @@ * Parses rule fixtures with Oxc's TypeScript ESTree nodes and TypeScript scope analysis, without a * compiler. Babel supplies the ESLint tokens and comments that Oxc's parser API does not expose. */ +const path = require('node:path'); const babelParser = require('@babel/eslint-parser'); const {analyze} = require('@typescript-eslint/scope-manager'); // Node can load Oxc's ESM entry point; Jest's CommonJS module loader cannot. const {parseSync, visitorKeys} = process.getBuiltinModule('module').createRequire(__filename)('oxc-parser'); +const JSX_EXTENSIONS = new Set(['.js', '.cjs', '.mjs', '.jsx', '.tsx', '.json']); +const NON_JSX_EXTENSIONS = new Set(['.ts', '.cts', '.mts']); /** * Oxc provides UTF-16 ranges, while ESLint also requires line and column locations on every node. @@ -43,14 +46,20 @@ function addLocations(node, lineStarts) { module.exports = { meta: {name: 'expensify-rule-test-parser', version: '1.0.0'}, parseForESLint(code, options) { + // Known extensions select the compiler's language variant; only unknown extensions use the JSX option. + const extension = path.extname(options.filePath ?? '').toLowerCase(); + const shouldParseJSX = JSX_EXTENSIONS.has(extension) || (!NON_JSX_EXTENSIONS.has(extension) && options.ecmaFeatures?.jsx === true); + const filePath = options.filePath ?? (shouldParseJSX ? 'fixture.tsx' : 'fixture.ts'); const tokenized = babelParser.parse(code, { ...options, + filePath, + ecmaFeatures: {...options.ecmaFeatures, jsx: shouldParseJSX}, requireConfigFile: false, babelOptions: { babelrc: false, configFile: false, parserOpts: { - plugins: ['typescript', 'jsx', 'decorators', 'decoratorAutoAccessors'], + plugins: ['typescript', ...(shouldParseJSX ? ['jsx'] : []), 'decorators', 'decoratorAutoAccessors'], // Oxc validates syntax. Babel only supplies tokens, so allow its recoverable error // for TypeScript parameter decorators, which standard JavaScript decorators forbid. errorRecovery: true, @@ -58,8 +67,8 @@ module.exports = { }, }); - const parsed = parseSync(options.filePath ?? 'fixture.tsx', code, { - lang: options.ecmaFeatures?.jsx ? 'tsx' : 'ts', + const parsed = parseSync(filePath, code, { + lang: shouldParseJSX ? 'tsx' : 'ts', sourceType: options.sourceType, range: true, preserveParens: false,