From 2f96a14b382158f256e83f985a126f39dfdabac5 Mon Sep 17 00:00:00 2001 From: Vincent Driessen Date: Thu, 16 Apr 2026 08:48:48 +0200 Subject: [PATCH] =?UTF-8?q?Upgrade=20ESLint=20v8=20=E2=86=92=20v9=20(#3366?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 1168 +++++------------ package.json | 1 + .../liveblocks-chat-sdk-adapter/.eslintrc.cjs | 9 - .../eslint.config.mjs | 15 + packages/liveblocks-client/.eslintrc.cjs | 47 - packages/liveblocks-client/eslint.config.mjs | 49 + packages/liveblocks-core/.eslintrc.cjs | 65 - packages/liveblocks-core/e2e/utils.ts | 1 - packages/liveblocks-core/eslint.config.mjs | 81 ++ packages/liveblocks-core/package.json | 1 - .../liveblocks-core/src/devtools/bridge.ts | 4 +- .../liveblocks-core/src/dupe-detection.ts | 2 +- .../src/lib/__tests__/signals/Signal.test.ts | 2 - .../liveblocks-core/src/lib/fancy-console.ts | 2 +- packages/liveblocks-core/src/lib/stringify.ts | 6 +- .../src/types/InferFromSchema.ts | 2 +- .../liveblocks-core/src/types/KnownKeys.ts | 2 +- .../src/types/LiveblocksError.ts | 4 +- packages/liveblocks-emails/.eslintrc.cjs | 25 - packages/liveblocks-emails/eslint.config.mjs | 32 + packages/liveblocks-emails/package.json | 1 + .../src/lib/__tests__/css-properties.test.ts | 2 - .../liveblocks-node-lexical/.eslintrc.cjs | 12 - .../liveblocks-node-lexical/eslint.config.mjs | 15 + .../liveblocks-node-prosemirror/.eslintrc.cjs | 12 - .../eslint.config.mjs | 15 + .../src/document.ts | 2 +- packages/liveblocks-node/.eslintrc.cjs | 12 - packages/liveblocks-node/eslint.config.mjs | 15 + packages/liveblocks-node/src/utils.ts | 2 +- .../liveblocks-react-blocknote/.eslintrc.cjs | 76 -- .../eslint.config.mjs | 73 ++ .../liveblocks-react-blocknote/package.json | 2 +- .../initialization/liveblocksEditorOptions.ts | 2 - packages/liveblocks-react-flow/.eslintrc.cjs | 62 - .../liveblocks-react-flow/eslint.config.mjs | 67 + packages/liveblocks-react-flow/package.json | 2 +- .../liveblocks-react-flow/src/lib/types.ts | 12 +- .../liveblocks-react-lexical/.eslintrc.cjs | 76 -- .../eslint.config.mjs | 73 ++ .../liveblocks-react-lexical/package.json | 2 +- .../wrap-selection-in-thread-mark-node.ts | 1 - .../liveblocks-react-tiptap/.eslintrc.cjs | 76 -- .../liveblocks-react-tiptap/eslint.config.mjs | 73 ++ packages/liveblocks-react-tiptap/package.json | 2 +- .../src/LiveblocksExtension.ts | 1 - .../src/ai/AiExtension.ts | 1 - .../src/ai/AiToolbar.tsx | 27 +- .../src/collaboration/collaboration.ts | 2 - .../collaboration/helpers/isChangeOrigin.ts | 2 +- packages/liveblocks-react-ui/.eslintrc.cjs | 76 -- .../liveblocks-react-ui/eslint.config.mjs | 73 ++ packages/liveblocks-react-ui/package.json | 2 +- .../src/primitives/Duration.tsx | 1 - packages/liveblocks-react/.eslintrc.cjs | 60 - packages/liveblocks-react/eslint.config.mjs | 71 + packages/liveblocks-react/package.json | 2 +- .../liveblocks-react/src/lib/use-initial.ts | 1 - packages/liveblocks-react/src/room.tsx | 4 + .../use-sync-external-store-with-selector.ts | 4 +- packages/liveblocks-redux/.eslintrc.cjs | 32 - packages/liveblocks-redux/eslint.config.mjs | 35 + packages/liveblocks-yjs/.eslintrc.cjs | 43 - packages/liveblocks-yjs/eslint.config.mjs | 44 + .../src/__tests__/index.test.ts | 1 - packages/liveblocks-yjs/src/awareness.ts | 4 - packages/liveblocks-zustand/.eslintrc.cjs | 41 - packages/liveblocks-zustand/eslint.config.mjs | 42 + shared/eslint-config/index.js | 137 +- shared/eslint-config/package.json | 16 +- shared/eslint-config/restricted-syntax.js | 5 +- tools/liveblocks-codemod/.eslintignore | 2 - tools/liveblocks-codemod/.eslintrc.cjs | 14 - tools/liveblocks-codemod/eslint.config.mjs | 18 + .../src/lib/find-and-replace.ts | 2 +- tools/liveblocks-devtools/.eslintrc.cjs | 27 - tools/liveblocks-devtools/eslint.config.mjs | 36 + tools/liveblocks-devtools/package.json | 3 +- .../src/devtools/components/Dialog.tsx | 3 +- .../devtools/components/ResizablePanel.tsx | 1 - .../src/devtools/components/Select.tsx | 6 +- .../src/devtools/components/Tabs.tsx | 6 +- .../src/devtools/components/Tooltip.tsx | 3 +- .../src/devtools/components/Tree.tsx | 75 +- .../src/devtools/contexts/CurrentRoom.tsx | 2 +- .../src/devtools/index.tsx | 4 +- .../src/devtools/panel.tsx | 2 +- .../src/devtools/tabs/yjs/to-y-node.ts | 6 +- .../src/devtools/tabs/yjs/to-yjs-tree-node.ts | 6 +- .../src/devtools/tabs/yjs/yflow/YItemNode.tsx | 26 +- .../liveblocks-devtools/src/lib/stringify.ts | 2 +- tools/liveblocks-devtools/src/lib/ydoc.ts | 1 - 92 files changed, 1346 insertions(+), 1831 deletions(-) delete mode 100644 packages/liveblocks-chat-sdk-adapter/.eslintrc.cjs create mode 100644 packages/liveblocks-chat-sdk-adapter/eslint.config.mjs delete mode 100644 packages/liveblocks-client/.eslintrc.cjs create mode 100644 packages/liveblocks-client/eslint.config.mjs delete mode 100644 packages/liveblocks-core/.eslintrc.cjs create mode 100644 packages/liveblocks-core/eslint.config.mjs delete mode 100644 packages/liveblocks-emails/.eslintrc.cjs create mode 100644 packages/liveblocks-emails/eslint.config.mjs delete mode 100644 packages/liveblocks-node-lexical/.eslintrc.cjs create mode 100644 packages/liveblocks-node-lexical/eslint.config.mjs delete mode 100644 packages/liveblocks-node-prosemirror/.eslintrc.cjs create mode 100644 packages/liveblocks-node-prosemirror/eslint.config.mjs delete mode 100644 packages/liveblocks-node/.eslintrc.cjs create mode 100644 packages/liveblocks-node/eslint.config.mjs delete mode 100644 packages/liveblocks-react-blocknote/.eslintrc.cjs create mode 100644 packages/liveblocks-react-blocknote/eslint.config.mjs delete mode 100644 packages/liveblocks-react-flow/.eslintrc.cjs create mode 100644 packages/liveblocks-react-flow/eslint.config.mjs delete mode 100644 packages/liveblocks-react-lexical/.eslintrc.cjs create mode 100644 packages/liveblocks-react-lexical/eslint.config.mjs delete mode 100644 packages/liveblocks-react-tiptap/.eslintrc.cjs create mode 100644 packages/liveblocks-react-tiptap/eslint.config.mjs delete mode 100644 packages/liveblocks-react-ui/.eslintrc.cjs create mode 100644 packages/liveblocks-react-ui/eslint.config.mjs delete mode 100644 packages/liveblocks-react/.eslintrc.cjs create mode 100644 packages/liveblocks-react/eslint.config.mjs delete mode 100644 packages/liveblocks-redux/.eslintrc.cjs create mode 100644 packages/liveblocks-redux/eslint.config.mjs delete mode 100644 packages/liveblocks-yjs/.eslintrc.cjs create mode 100644 packages/liveblocks-yjs/eslint.config.mjs delete mode 100644 packages/liveblocks-zustand/.eslintrc.cjs create mode 100644 packages/liveblocks-zustand/eslint.config.mjs delete mode 100644 tools/liveblocks-codemod/.eslintignore delete mode 100644 tools/liveblocks-codemod/.eslintrc.cjs create mode 100644 tools/liveblocks-codemod/eslint.config.mjs delete mode 100644 tools/liveblocks-devtools/.eslintrc.cjs create mode 100644 tools/liveblocks-devtools/eslint.config.mjs diff --git a/package-lock.json b/package-lock.json index fa9436769dd..c6d2fe7d9fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@types/react-dom": "^18.3.5", "@vitest/coverage-istanbul": "^4.1.4", "dependency-cruiser": "^17.3.10", + "eslint": "^9.39.4", "happy-dom": "^20.9.0", "prettier": "^3.8.3", "publint": "^0.3.18", @@ -114,21 +115,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "e2e/next-ai-kitchen-sink/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, "e2e/next-ai-kitchen-sink/node_modules/eslint": { "version": "9.26.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", @@ -403,207 +389,6 @@ "typescript": "^5" } }, - "e2e/next-react-flow-kitchen-sink/node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/@eslint/eslintrc": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", - "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.1", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/@eslint/js": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", - "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/eslint": { - "version": "9.39.4", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", - "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.2", - "@eslint/config-helpers": "^0.4.2", - "@eslint/core": "^0.17.0", - "@eslint/eslintrc": "^3.3.5", - "@eslint/js": "9.39.4", - "@eslint/plugin-kit": "^0.4.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, "e2e/next-react-flow-kitchen-sink/node_modules/eslint-config-next": { "version": "16.1.4", "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-16.1.4.tgz", @@ -664,90 +449,6 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "e2e/next-react-flow-kitchen-sink/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "e2e/next-react-flow-kitchen-sink/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "e2e/next-react-flow-kitchen-sink/node_modules/react": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", @@ -953,37 +654,6 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, - "e2e/next-sandbox/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "e2e/next-sandbox/node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, "e2e/next-sandbox/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -2776,38 +2446,56 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/js": { - "version": "8.57.0", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2937,7 +2625,6 @@ "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" @@ -2947,7 +2634,6 @@ "version": "0.16.6", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", @@ -2961,7 +2647,6 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -2971,18 +2656,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "license": "Apache-2.0", @@ -2994,15 +2667,10 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "license": "BSD-3-Clause" - }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" @@ -18084,79 +17752,14 @@ "dev": true, "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.13.1", - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/type-utils": "7.13.1", - "@typescript-eslint/utils": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "5.3.1", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.13.1", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/project-service": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz", - "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.2.tgz", + "integrity": "sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.53.1", - "@typescript-eslint/types": "^8.53.1", + "@typescript-eslint/tsconfig-utils": "^8.58.2", + "@typescript-eslint/types": "^8.58.2", "debug": "^4.4.3" }, "engines": { @@ -18167,14 +17770,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", - "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18188,7 +17790,6 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -18202,26 +17803,10 @@ } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.13.1", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz", - "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.2.tgz", + "integrity": "sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -18231,139 +17816,13 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.13.1", - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "7.13.1", - "@typescript-eslint/utils": "7.13.1", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.13.1", - "license": "MIT", - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.13.1", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/visitor-keys": "7.13.1", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.6.2", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.13.1", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.13.1", - "@typescript-eslint/types": "7.13.1", - "@typescript-eslint/typescript-estree": "7.13.1" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.13.1", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "7.13.1", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/@vitest/coverage-istanbul": { "version": "4.1.4", @@ -18699,7 +18158,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -20284,7 +19745,9 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -20943,16 +20406,6 @@ "redux": "^4.1.1" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/dom-accessibility-api": { "version": "0.5.14", "dev": true, @@ -21768,56 +21221,62 @@ } }, "node_modules/eslint": { - "version": "8.57.0", + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-import-resolver-node": { @@ -22051,14 +21510,16 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "node_modules/eslint-plugin-react/node_modules/doctrine": { @@ -22098,16 +21559,10 @@ "semver": "bin/semver.js" } }, - "node_modules/eslint-plugin-rulesdir": { - "version": "0.2.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/eslint-plugin-simple-import-sort": { - "version": "12.1.0", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-13.0.0.tgz", + "integrity": "sha512-McAc+/Nlvcg4byY/CABGH8kqnefWBj8s3JA2okEtz8ixbECQgU46p0HkTUKa4YS7wvgGceimlc34p1nXqbWqtA==", "license": "MIT", "peerDependencies": { "eslint": ">=5.0.0" @@ -22117,7 +21572,6 @@ "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", @@ -22140,6 +21594,57 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "license": "MIT", @@ -22154,30 +21659,67 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/eslint/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/espree": { - "version": "9.6.1", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -22678,6 +22220,7 @@ }, "node_modules/file-entry-cache": { "version": "6.0.1", + "devOptional": true, "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" @@ -22902,6 +22445,7 @@ }, "node_modules/flat-cache": { "version": "3.0.4", + "devOptional": true, "license": "MIT", "dependencies": { "flatted": "^3.1.0", @@ -23369,10 +22913,6 @@ "version": "4.2.10", "license": "ISC" }, - "node_modules/graphemer": { - "version": "1.4.0", - "license": "MIT" - }, "node_modules/graphql": { "version": "16.8.1", "dev": true, @@ -24114,7 +23654,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "5.2.4", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -24631,13 +24173,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-plain-obj": { "version": "1.1.0", "license": "MIT", @@ -25058,7 +24593,7 @@ }, "node_modules/jiti": { "version": "1.21.3", - "dev": true, + "devOptional": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -26985,7 +26520,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -30435,6 +29972,7 @@ }, "node_modules/rimraf": { "version": "3.0.2", + "devOptional": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -32496,10 +32034,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/text-table": { - "version": "0.2.0", - "license": "MIT" - }, "node_modules/thenby": { "version": "1.3.4", "license": "Apache-2.0" @@ -32551,7 +32085,6 @@ "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -32568,7 +32101,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -32586,7 +32118,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -32735,16 +32266,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/ts-api-utils": { - "version": "1.3.0", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, "node_modules/ts-interface-checker": { "version": "0.1.13", "license": "Apache-2.0" @@ -33670,16 +33191,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.1.tgz", - "integrity": "sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.2.tgz", + "integrity": "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.53.1", - "@typescript-eslint/parser": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/utils": "8.53.1" + "@typescript-eslint/eslint-plugin": "8.58.2", + "@typescript-eslint/parser": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33689,25 +33209,24 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", - "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.2.tgz", + "integrity": "sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/type-utils": "8.53.1", - "@typescript-eslint/utils": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/type-utils": "8.58.2", + "@typescript-eslint/utils": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33717,22 +33236,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.53.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "@typescript-eslint/parser": "^8.58.2", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", - "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.2.tgz", + "integrity": "sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3" }, "engines": { @@ -33743,19 +33261,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz", - "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.2.tgz", + "integrity": "sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1" + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33766,17 +33283,16 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz", - "integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.2.tgz", + "integrity": "sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1", - "@typescript-eslint/utils": "8.53.1", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2", + "@typescript-eslint/utils": "8.58.2", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33786,15 +33302,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", - "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.2.tgz", + "integrity": "sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33805,21 +33320,20 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz", - "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.2.tgz", + "integrity": "sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.53.1", - "@typescript-eslint/tsconfig-utils": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/visitor-keys": "8.53.1", + "@typescript-eslint/project-service": "8.58.2", + "@typescript-eslint/tsconfig-utils": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/visitor-keys": "8.58.2", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33829,20 +33343,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz", - "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.2.tgz", + "integrity": "sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.53.1", - "@typescript-eslint/types": "8.53.1", - "@typescript-eslint/typescript-estree": "8.53.1" + "@typescript-eslint/scope-manager": "8.58.2", + "@typescript-eslint/types": "8.58.2", + "@typescript-eslint/typescript-estree": "8.58.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33852,19 +33365,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.53.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz", - "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==", - "dev": true, + "version": "8.58.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.2.tgz", + "integrity": "sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.53.1", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.58.2", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -33874,21 +33386,31 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/typescript-eslint/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/typescript-eslint/node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -33903,80 +33425,45 @@ } }, "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "license": "Apache-2.0", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, - "node_modules/typescript-eslint/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/typescript-eslint/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/typescript-eslint/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/typescript-eslint/node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -33985,28 +33472,10 @@ "node": ">=10" } }, - "node_modules/typescript-eslint/node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, "node_modules/typescript-eslint/node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", - "dev": true, + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "license": "MIT", "engines": { "node": ">=18.12" @@ -36006,7 +35475,6 @@ "@liveblocks/query-parser": "^0.1.1", "@liveblocks/vitest-config": "*", "@types/ws": "^8.5.10", - "eslint-plugin-rulesdir": "^0.2.2", "fast-check": "^4.6.0", "msw": "^2.10.4", "ws": "^8.17.1" @@ -36027,6 +35495,7 @@ "@liveblocks/eslint-config": "*", "@liveblocks/vitest-config": "*", "csstype": "3.0.2", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4" }, "peerDependencies": { @@ -36126,7 +35595,7 @@ "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "date-fns": "^3.6.0", - "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-hooks": "^5.2.0", "itertools": "^2.3.2", "msw": "^2.10.4", "react-error-boundary": "^4.0.13" @@ -36165,7 +35634,7 @@ "@liveblocks/vitest-config": "*", "@testing-library/jest-dom": "^5.16.5", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", @@ -36209,7 +35678,7 @@ "@testing-library/react": "^13.1.1", "@xyflow/react": "^12.10.1", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", "stylelint-order": "^6.0.3", @@ -36280,7 +35749,7 @@ "@liveblocks/vitest-config": "*", "@testing-library/jest-dom": "^5.16.5", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", @@ -37927,7 +37396,7 @@ "@liveblocks/vitest-config": "*", "@testing-library/jest-dom": "^5.16.5", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", @@ -37978,10 +37447,6 @@ "react-dom": ">=16.8.0" } }, - "packages/liveblocks-react-tiptap/node_modules/@floating-ui/utils": { - "version": "0.2.8", - "license": "MIT" - }, "packages/liveblocks-react-tiptap/node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -39582,7 +39047,7 @@ "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", @@ -39630,10 +39095,6 @@ "react-dom": ">=16.8.0" } }, - "packages/liveblocks-react-ui/node_modules/@floating-ui/utils": { - "version": "0.2.2", - "license": "MIT" - }, "packages/liveblocks-react-ui/node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -40218,11 +39679,11 @@ "shared/eslint-config": { "name": "@liveblocks/eslint-config", "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", - "eslint": "^8.56.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-simple-import-sort": "^12.1.0" + "@eslint/js": "^9.39.4", + "eslint": "^9.39.4", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-simple-import-sort": "^13.0.0", + "typescript-eslint": "^8.58.2" } }, "shared/rollup-config": { @@ -40669,9 +40130,10 @@ "yjs": "^13.6.14" }, "devDependencies": { + "@liveblocks/eslint-config": "*", "@types/webextension-polyfill": "^0.10.0", "autoprefixer": "^10.4.13", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "postcss": "^8.4.20", "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "^3.2.4", diff --git a/package.json b/package.json index 7fda9ab0f51..fd3d19eb412 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@types/react-dom": "^18.3.5", "@vitest/coverage-istanbul": "^4.1.4", "dependency-cruiser": "^17.3.10", + "eslint": "^9.39.4", "happy-dom": "^20.9.0", "prettier": "^3.8.3", "publint": "^0.3.18", diff --git a/packages/liveblocks-chat-sdk-adapter/.eslintrc.cjs b/packages/liveblocks-chat-sdk-adapter/.eslintrc.cjs deleted file mode 100644 index 4fce48fbbe5..00000000000 --- a/packages/liveblocks-chat-sdk-adapter/.eslintrc.cjs +++ /dev/null @@ -1,9 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - }, -}; diff --git a/packages/liveblocks-chat-sdk-adapter/eslint.config.mjs b/packages/liveblocks-chat-sdk-adapter/eslint.config.mjs new file mode 100644 index 00000000000..b76be4b3cae --- /dev/null +++ b/packages/liveblocks-chat-sdk-adapter/eslint.config.mjs @@ -0,0 +1,15 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + }, + }, +]; diff --git a/packages/liveblocks-client/.eslintrc.cjs b/packages/liveblocks-client/.eslintrc.cjs deleted file mode 100644 index ff7df4db5a3..00000000000 --- a/packages/liveblocks-client/.eslintrc.cjs +++ /dev/null @@ -1,47 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "CallExpression[callee.object.name='JSON'][callee.property.name='parse']", - message: - "Using `JSON.parse()` is type-unsafe. Prefer using the `tryParseJson()` utility method (from `src/utils`).", - }, - { - selector: "TSNonNullExpression", - message: - "Non-null assertions mask real problems. Please use `nn(...)` (from src/assert.ts) instead.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - /* None yet 😇 ! */ - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - /* None yet 😇 ! */ - }, - overrides: [ - { - files: ["src/__tests__/**"], - - // Special config for test files - rules: { - "no-restricted-syntax": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - }, - }, - ], -}; diff --git a/packages/liveblocks-client/eslint.config.mjs b/packages/liveblocks-client/eslint.config.mjs new file mode 100644 index 00000000000..0196f1d24fe --- /dev/null +++ b/packages/liveblocks-client/eslint.config.mjs @@ -0,0 +1,49 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "CallExpression[callee.object.name='JSON'][callee.property.name='parse']", + message: + "Using `JSON.parse()` is type-unsafe. Prefer using the `tryParseJson()` utility method (from `src/utils`).", + }, + { + selector: "TSNonNullExpression", + message: + "Non-null assertions mask real problems. Please use `nn(...)` (from src/assert.ts) instead.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + /* None yet 😇 ! */ + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + /* None yet 😇 ! */ + }, + }, + + { + files: ["src/__tests__/**"], + + // Special config for test files + rules: { + "no-restricted-syntax": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + }, + }, +]; diff --git a/packages/liveblocks-core/.eslintrc.cjs b/packages/liveblocks-core/.eslintrc.cjs deleted file mode 100644 index 1731674bf1f..00000000000 --- a/packages/liveblocks-core/.eslintrc.cjs +++ /dev/null @@ -1,65 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -const rulesDirPlugin = require("eslint-plugin-rulesdir"); -rulesDirPlugin.RULES_DIR = "./rules"; - -module.exports = { - root: true, - plugins: ["eslint-plugin-rulesdir"], - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "CallExpression[callee.object.name='JSON'][callee.property.name='parse']", - message: - "Using `JSON.parse()` is type-unsafe. Prefer using the `tryParseJson()` utility method (from `src/utils`).", - }, - { - selector: "TSNonNullExpression", - message: - "Non-null assertions mask real problems. Please use `nn(...)` (from src/assert.ts) instead.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-namespace": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Always use fancy console logging (with the Liveblocks logo in there) - "rulesdir/console-must-be-fancy": "error", - }, - overrides: [ - { - files: ["src/**/__tests__/**", "e2e/**"], - - // Special config for test files - rules: { - "no-restricted-syntax": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - - // Ideally, we should remove these overrides, since they are still - // useful to catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - - // No need for fancy console logging in tests - "rulesdir/console-must-be-fancy": "off", - }, - }, - ], -}; diff --git a/packages/liveblocks-core/e2e/utils.ts b/packages/liveblocks-core/e2e/utils.ts index fd2b665ddb5..48eb6760f00 100644 --- a/packages/liveblocks-core/e2e/utils.ts +++ b/packages/liveblocks-core/e2e/utils.ts @@ -74,7 +74,6 @@ async function initializeRoomForTest< baseUrl: BASE_URL, }); - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument const { room, leave } = client.enterRoom(roomId, { initialPresence, initialStorage, diff --git a/packages/liveblocks-core/eslint.config.mjs b/packages/liveblocks-core/eslint.config.mjs new file mode 100644 index 00000000000..1e13ad6a9ca --- /dev/null +++ b/packages/liveblocks-core/eslint.config.mjs @@ -0,0 +1,81 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +import consoleMustBeFancy from "./rules/console-must-be-fancy.cjs"; + +export default [ + ...makeConfig(), + + { + plugins: { + local: { + rules: { + "console-must-be-fancy": consoleMustBeFancy, + }, + }, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "CallExpression[callee.object.name='JSON'][callee.property.name='parse']", + message: + "Using `JSON.parse()` is type-unsafe. Prefer using the `tryParseJson()` utility method (from `src/utils`).", + }, + { + selector: "TSNonNullExpression", + message: + "Non-null assertions mask real problems. Please use `nn(...)` (from src/assert.ts) instead.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-namespace": "off", + "@typescript-eslint/unbound-method": "off", + + // TODO: New rules in typescript-eslint v8's recommendedTypeChecked + // that weren't in v7. Disabled during the v8 migration to keep it + // clean, but we should revisit and enable these later. + "@typescript-eslint/only-throw-error": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + "@typescript-eslint/no-duplicate-type-constituents": "off", + "@typescript-eslint/no-unused-expressions": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Always use fancy console logging (with the Liveblocks logo in there) + "local/console-must-be-fancy": "error", + }, + }, + + { + files: ["src/**/__tests__/**", "e2e/**"], + + // Special config for test files + rules: { + "no-restricted-syntax": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + + // Ideally, we should remove these overrides, since they are still + // useful to catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + + // No need for fancy console logging in tests + "local/console-must-be-fancy": "off", + }, + }, +]; diff --git a/packages/liveblocks-core/package.json b/packages/liveblocks-core/package.json index 163854f98f0..a100284a783 100644 --- a/packages/liveblocks-core/package.json +++ b/packages/liveblocks-core/package.json @@ -53,7 +53,6 @@ "@liveblocks/query-parser": "^0.1.1", "@liveblocks/vitest-config": "*", "@types/ws": "^8.5.10", - "eslint-plugin-rulesdir": "^0.2.2", "fast-check": "^4.6.0", "msw": "^2.10.4", "ws": "^8.17.1" diff --git a/packages/liveblocks-core/src/devtools/bridge.ts b/packages/liveblocks-core/src/devtools/bridge.ts index 17c16cf9742..ad46b9d3ee5 100644 --- a/packages/liveblocks-core/src/devtools/bridge.ts +++ b/packages/liveblocks-core/src/devtools/bridge.ts @@ -32,7 +32,7 @@ export function sendToPanel( if (!(options?.force || _bridgeActive)) { /* - // eslint-disable-next-line rulesdir/console-must-be-fancy + // eslint-disable-next-line local/console-must-be-fancy console.log( "%c[client → panel] %c%s", "color: green", @@ -44,7 +44,7 @@ export function sendToPanel( return; } /* - // eslint-disable-next-line rulesdir/console-must-be-fancy + // eslint-disable-next-line local/console-must-be-fancy console.log( "%c[client → panel] %c%s", "color: green", diff --git a/packages/liveblocks-core/src/dupe-detection.ts b/packages/liveblocks-core/src/dupe-detection.ts index 80e8476c3a1..eb5e2341069 100644 --- a/packages/liveblocks-core/src/dupe-detection.ts +++ b/packages/liveblocks-core/src/dupe-detection.ts @@ -16,7 +16,7 @@ const SPACE = " "; // Important space to make sure links in errors are clickable function error(msg: string): void { if (process.env.NODE_ENV === "production") { - // eslint-disable-next-line rulesdir/console-must-be-fancy + // eslint-disable-next-line local/console-must-be-fancy console.error(msg); } else { throw new Error(msg); diff --git a/packages/liveblocks-core/src/lib/__tests__/signals/Signal.test.ts b/packages/liveblocks-core/src/lib/__tests__/signals/Signal.test.ts index 62ffb66e429..96e4e27c062 100644 --- a/packages/liveblocks-core/src/lib/__tests__/signals/Signal.test.ts +++ b/packages/liveblocks-core/src/lib/__tests__/signals/Signal.test.ts @@ -134,7 +134,6 @@ test("[prop] will freeze all given values", () => { const signal = new Signal(init); expect(signal.get()).toBe(init); - /* eslint-disable @typescript-eslint/no-unsafe-return */ expect(() => { // @ts-expect-error - deliberately set invalid prop signal.get().abc = 123; @@ -153,7 +152,6 @@ test("[prop] will freeze all given values", () => { // @ts-expect-error - get prop expect(signal.get().xyz).toBe(undefined); - /* eslint-enable @typescript-eslint/no-unsafe-return */ } ) ); diff --git a/packages/liveblocks-core/src/lib/fancy-console.ts b/packages/liveblocks-core/src/lib/fancy-console.ts index 5f2c1a22e4e..a1358b0606e 100644 --- a/packages/liveblocks-core/src/lib/fancy-console.ts +++ b/packages/liveblocks-core/src/lib/fancy-console.ts @@ -1,4 +1,4 @@ -/* eslint-disable rulesdir/console-must-be-fancy */ +/* eslint-disable local/console-must-be-fancy */ const badge = "background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;"; diff --git a/packages/liveblocks-core/src/lib/stringify.ts b/packages/liveblocks-core/src/lib/stringify.ts index 60ae3ae5b31..0a4ea891cc3 100644 --- a/packages/liveblocks-core/src/lib/stringify.ts +++ b/packages/liveblocks-core/src/lib/stringify.ts @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ function replacer(_key: string, value: unknown) { return value !== null && typeof value === "object" && !Array.isArray(value) @@ -30,10 +28,10 @@ export function stringifyOrLog(value: unknown): string { try { return JSON.stringify(value); } catch (err) { - /* eslint-disable rulesdir/console-must-be-fancy */ + /* eslint-disable local/console-must-be-fancy */ console.error(`Could not stringify: ${(err as Error).message}`); console.error(value); - /* eslint-enable rulesdir/console-must-be-fancy */ + /* eslint-enable local/console-must-be-fancy */ throw err; } } diff --git a/packages/liveblocks-core/src/types/InferFromSchema.ts b/packages/liveblocks-core/src/types/InferFromSchema.ts index 11f482428b3..1dfe4fb1906 100644 --- a/packages/liveblocks-core/src/types/InferFromSchema.ts +++ b/packages/liveblocks-core/src/types/InferFromSchema.ts @@ -5,7 +5,7 @@ import type { Resolve } from "../lib/Resolve"; export type JSONObjectSchema7 = JSONSchema7 & { type: "object" }; -// eslint-disable-next-line @typescript-eslint/ban-types +// eslint-disable-next-line @typescript-eslint/no-empty-object-type type NoFields = {}; type Infer = T extends JSONSchema7 ? InferFromSchema : never; diff --git a/packages/liveblocks-core/src/types/KnownKeys.ts b/packages/liveblocks-core/src/types/KnownKeys.ts index 7fa2e003e43..26949439bd1 100644 --- a/packages/liveblocks-core/src/types/KnownKeys.ts +++ b/packages/liveblocks-core/src/types/KnownKeys.ts @@ -3,7 +3,7 @@ * any index signature (e.g. `[key: string]: ...`). */ export type KnownKeys = keyof { - // eslint-disable-next-line @typescript-eslint/ban-types + // eslint-disable-next-line @typescript-eslint/no-empty-object-type [K in keyof T as {} extends Record ? never : K]: true; } & string; diff --git a/packages/liveblocks-core/src/types/LiveblocksError.ts b/packages/liveblocks-core/src/types/LiveblocksError.ts index 24231467aa4..0fc0bcbeca4 100644 --- a/packages/liveblocks-core/src/types/LiveblocksError.ts +++ b/packages/liveblocks-core/src/types/LiveblocksError.ts @@ -7,12 +7,12 @@ import type { Patchable } from "./Patchable"; type AiConnectionErrorContext = { type: "AI_CONNECTION_ERROR"; - code: -1 | 4001 | (number & {}); // eslint-disable-line @typescript-eslint/ban-types + code: -1 | 4001 | (number & {}); }; type RoomConnectionErrorContext = { type: "ROOM_CONNECTION_ERROR"; - code: -1 | 4001 | 4005 | 4006 | (number & {}); // eslint-disable-line @typescript-eslint/ban-types + code: -1 | 4001 | 4005 | 4006 | (number & {}); roomId: string; }; diff --git a/packages/liveblocks-emails/.eslintrc.cjs b/packages/liveblocks-emails/.eslintrc.cjs deleted file mode 100644 index 757c394e461..00000000000 --- a/packages/liveblocks-emails/.eslintrc.cjs +++ /dev/null @@ -1,25 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react-hooks"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - ], - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - }, -}; diff --git a/packages/liveblocks-emails/eslint.config.mjs b/packages/liveblocks-emails/eslint.config.mjs new file mode 100644 index 00000000000..143ac76c20c --- /dev/null +++ b/packages/liveblocks-emails/eslint.config.mjs @@ -0,0 +1,32 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + "react-hooks": reactHooks, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + ], + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + }, + }, +]; diff --git a/packages/liveblocks-emails/package.json b/packages/liveblocks-emails/package.json index 5e1bb34483e..74913eb358a 100644 --- a/packages/liveblocks-emails/package.json +++ b/packages/liveblocks-emails/package.json @@ -56,6 +56,7 @@ "@liveblocks/eslint-config": "*", "@liveblocks/vitest-config": "*", "csstype": "3.0.2", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4" }, "sideEffects": false, diff --git a/packages/liveblocks-emails/src/lib/__tests__/css-properties.test.ts b/packages/liveblocks-emails/src/lib/__tests__/css-properties.test.ts index 1a7b7e37d75..cc0cea68b6a 100644 --- a/packages/liveblocks-emails/src/lib/__tests__/css-properties.test.ts +++ b/packages/liveblocks-emails/src/lib/__tests__/css-properties.test.ts @@ -66,11 +66,9 @@ describe("to inline css string", () => { test("should ignore null, undefined, and boolean values", () => { const styles: CSSProperties = { color: "red", - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - `null` is not a legal value, try to use it anyway backgroundColor: null, display: undefined, - // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error - `true` is not a legal value, try to use it anyway visibility: true, opacity: 0.5, diff --git a/packages/liveblocks-node-lexical/.eslintrc.cjs b/packages/liveblocks-node-lexical/.eslintrc.cjs deleted file mode 100644 index ae2147270af..00000000000 --- a/packages/liveblocks-node-lexical/.eslintrc.cjs +++ /dev/null @@ -1,12 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - }, -}; diff --git a/packages/liveblocks-node-lexical/eslint.config.mjs b/packages/liveblocks-node-lexical/eslint.config.mjs new file mode 100644 index 00000000000..b76be4b3cae --- /dev/null +++ b/packages/liveblocks-node-lexical/eslint.config.mjs @@ -0,0 +1,15 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + }, + }, +]; diff --git a/packages/liveblocks-node-prosemirror/.eslintrc.cjs b/packages/liveblocks-node-prosemirror/.eslintrc.cjs deleted file mode 100644 index ae2147270af..00000000000 --- a/packages/liveblocks-node-prosemirror/.eslintrc.cjs +++ /dev/null @@ -1,12 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - }, -}; diff --git a/packages/liveblocks-node-prosemirror/eslint.config.mjs b/packages/liveblocks-node-prosemirror/eslint.config.mjs new file mode 100644 index 00000000000..b76be4b3cae --- /dev/null +++ b/packages/liveblocks-node-prosemirror/eslint.config.mjs @@ -0,0 +1,15 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + }, + }, +]; diff --git a/packages/liveblocks-node-prosemirror/src/document.ts b/packages/liveblocks-node-prosemirror/src/document.ts index 2b61e877b92..bc63ba09240 100644 --- a/packages/liveblocks-node-prosemirror/src/document.ts +++ b/packages/liveblocks-node-prosemirror/src/document.ts @@ -182,7 +182,7 @@ export async function withProsemirrorDocument( } const node = createDocumentFromContent(content, schema); if (!node) { - throw "Invalid content"; + throw new Error("Invalid content"); } return this.update((doc, tr) => { tr.delete(0, doc.content.size); diff --git a/packages/liveblocks-node/.eslintrc.cjs b/packages/liveblocks-node/.eslintrc.cjs deleted file mode 100644 index ae2147270af..00000000000 --- a/packages/liveblocks-node/.eslintrc.cjs +++ /dev/null @@ -1,12 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - }, -}; diff --git a/packages/liveblocks-node/eslint.config.mjs b/packages/liveblocks-node/eslint.config.mjs new file mode 100644 index 00000000000..b76be4b3cae --- /dev/null +++ b/packages/liveblocks-node/eslint.config.mjs @@ -0,0 +1,15 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + }, + }, +]; diff --git a/packages/liveblocks-node/src/utils.ts b/packages/liveblocks-node/src/utils.ts index f9d9f916dfa..065237114ed 100644 --- a/packages/liveblocks-node/src/utils.ts +++ b/packages/liveblocks-node/src/utils.ts @@ -3,7 +3,7 @@ const DEFAULT_BASE_URL = "https://api.liveblocks.io"; // Valid alphabet for secret/public keys const VALID_KEY_CHARS_REGEX = /^[\w-]+$/; -export function getBaseUrl(baseUrl?: string | undefined): string { +export function getBaseUrl(baseUrl?: string): string { if ( typeof baseUrl === "string" && baseUrl.startsWith("http") // Must be http or https URL diff --git a/packages/liveblocks-react-blocknote/.eslintrc.cjs b/packages/liveblocks-react-blocknote/.eslintrc.cjs deleted file mode 100644 index f2ebc3e9fd9..00000000000 --- a/packages/liveblocks-react-blocknote/.eslintrc.cjs +++ /dev/null @@ -1,76 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react", "react-hooks"], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - version: "detect", - }, - }, - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "react/jsx-key": ["error", { checkFragmentShorthand: true }], - "react/no-unescaped-entities": "error", - "react/no-unknown-property": "error", - - // Relax promise rules given how we use them in this project - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-misused-promises": "off", - }, - - overrides: [ - { - files: ["src/**/__tests__/**"], - - rules: { - // Ideally, enable these lint rules again later, as they are useful to - // catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-floating-promises": "off", - }, - }, - { - files: ["rollup.config.mjs"], - env: { - node: true, - }, - }, - ], -}; diff --git a/packages/liveblocks-react-blocknote/eslint.config.mjs b/packages/liveblocks-react-blocknote/eslint.config.mjs new file mode 100644 index 00000000000..0e45a93e9d4 --- /dev/null +++ b/packages/liveblocks-react-blocknote/eslint.config.mjs @@ -0,0 +1,73 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + react, + "react-hooks": reactHooks, + }, + + settings: { + react: { + version: "detect", + }, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + "react/jsx-key": ["error", { checkFragmentShorthand: true }], + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + + // Relax promise rules given how we use them in this project + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-misused-promises": "off", + }, + }, + + { + files: ["src/**/__tests__/**"], + + rules: { + // Ideally, enable these lint rules again later, as they are useful to + // catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-floating-promises": "off", + }, + }, +]; diff --git a/packages/liveblocks-react-blocknote/package.json b/packages/liveblocks-react-blocknote/package.json index 8451023db02..a88e5d5c598 100644 --- a/packages/liveblocks-react-blocknote/package.json +++ b/packages/liveblocks-react-blocknote/package.json @@ -76,7 +76,7 @@ "@liveblocks/rollup-config": "*", "@testing-library/jest-dom": "^5.16.5", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", diff --git a/packages/liveblocks-react-blocknote/src/initialization/liveblocksEditorOptions.ts b/packages/liveblocks-react-blocknote/src/initialization/liveblocksEditorOptions.ts index 767acb5feb4..3b207c863f5 100644 --- a/packages/liveblocks-react-blocknote/src/initialization/liveblocksEditorOptions.ts +++ b/packages/liveblocks-react-blocknote/src/initialization/liveblocksEditorOptions.ts @@ -20,7 +20,6 @@ export const withLiveblocksEditorOptions = < S extends StyleSchema = DefaultStyleSchema, >( liveblocksExtension: Extension, - // eslint-disable-next-line @typescript-eslint/no-explicit-any blocknoteOptions: Partial> = {}, liveblocksOptions: Partial<{ mentions: boolean }> = {} ): Partial> => { @@ -37,7 +36,6 @@ export const withLiveblocksEditorOptions = < // add the liveblocks extension extensions: [ - // eslint-disable-next-line @typescript-eslint/no-unsafe-call createExtension({ key: "liveblocksExtension", tiptapExtensions: [liveblocksExtension], diff --git a/packages/liveblocks-react-flow/.eslintrc.cjs b/packages/liveblocks-react-flow/.eslintrc.cjs deleted file mode 100644 index ec54ae38f96..00000000000 --- a/packages/liveblocks-react-flow/.eslintrc.cjs +++ /dev/null @@ -1,62 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react-hooks"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='useLayoutEffect']", - message: - "useLayoutEffect triggers a warning when executed on the server on React <=18.2.0. Import it from './lib/use-layout-effect' instead.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": [ - "error", - { additionalHooks: "useMutation" }, - ], - }, - - overrides: [ - { - files: ["src/**/__tests__/**"], - - rules: { - // Ideally, enable these lint rules again later, as they are useful to - // catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-floating-promises": "off", - }, - }, - ], -}; diff --git a/packages/liveblocks-react-flow/eslint.config.mjs b/packages/liveblocks-react-flow/eslint.config.mjs new file mode 100644 index 00000000000..2d227d94c4c --- /dev/null +++ b/packages/liveblocks-react-flow/eslint.config.mjs @@ -0,0 +1,67 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + "react-hooks": reactHooks, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='useLayoutEffect']", + message: + "useLayoutEffect triggers a warning when executed on the server on React <=18.2.0. Import it from './lib/use-layout-effect' instead.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": [ + "error", + { additionalHooks: "useMutation" }, + ], + }, + }, + + { + files: ["src/**/__tests__/**"], + + rules: { + // Ideally, enable these lint rules again later, as they are useful to + // catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-floating-promises": "off", + }, + }, +]; diff --git a/packages/liveblocks-react-flow/package.json b/packages/liveblocks-react-flow/package.json index 0c8fe1d6b83..2507e6905c1 100644 --- a/packages/liveblocks-react-flow/package.json +++ b/packages/liveblocks-react-flow/package.json @@ -90,7 +90,7 @@ "@testing-library/react": "^13.1.1", "@xyflow/react": "^12.10.1", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", "stylelint-order": "^6.0.3", diff --git a/packages/liveblocks-react-flow/src/lib/types.ts b/packages/liveblocks-react-flow/src/lib/types.ts index 9991f19fe47..6a3199547bb 100644 --- a/packages/liveblocks-react-flow/src/lib/types.ts +++ b/packages/liveblocks-react-flow/src/lib/types.ts @@ -21,10 +21,7 @@ type InferNodeTypeLiterals = : T : never; -type NodeTypeLiterals = - | (string & {}) // eslint-disable-line @typescript-eslint/ban-types - | "*" - | InferNodeTypeLiterals; +type NodeTypeLiterals = (string & {}) | "*" | InferNodeTypeLiterals; type InferEdgeTypeLiterals = E extends Edge @@ -33,10 +30,7 @@ type InferEdgeTypeLiterals = : T : never; -type EdgeTypeLiterals = - | (string & {}) // eslint-disable-line @typescript-eslint/ban-types - | "*" - | InferEdgeTypeLiterals; +type EdgeTypeLiterals = (string & {}) | "*" | InferEdgeTypeLiterals; export type NodeSyncConfig = { [key in NodeTypeLiterals]?: SyncConfig; @@ -63,7 +57,7 @@ type ToLson = [S] extends [false] ? T | undefined : [S] extends ["atomic"] ? T : T extends JsonScalar ? T : - // eslint-disable-next-line @typescript-eslint/ban-types + // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type T extends Date | RegExp | Function | Promise | WeakMap | WeakSet | Map | Set ? never : T extends ReadonlyArray ? ToLiveList : T extends object ? ToLiveObject : diff --git a/packages/liveblocks-react-lexical/.eslintrc.cjs b/packages/liveblocks-react-lexical/.eslintrc.cjs deleted file mode 100644 index 958303bf03e..00000000000 --- a/packages/liveblocks-react-lexical/.eslintrc.cjs +++ /dev/null @@ -1,76 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react", "react-hooks"], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - version: "detect", - }, - }, - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "react/jsx-key": ["error", { checkFragmentShorthand: true }], - "react/no-unescaped-entities": "error", - "react/no-unknown-property": "error", - - // Relax promise rules given how we use them in this project - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-misused-promises": "off", - }, - - overrides: [ - { - files: ["src/**/__tests__/**"], - - rules: { - // Ideally, enable these lint rules again later, as they are useful to - // catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-floating-promises": "off", - }, - }, - { - files: ["rollup.config.js"], - env: { - node: true, - }, - }, - ], -}; diff --git a/packages/liveblocks-react-lexical/eslint.config.mjs b/packages/liveblocks-react-lexical/eslint.config.mjs new file mode 100644 index 00000000000..0e45a93e9d4 --- /dev/null +++ b/packages/liveblocks-react-lexical/eslint.config.mjs @@ -0,0 +1,73 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + react, + "react-hooks": reactHooks, + }, + + settings: { + react: { + version: "detect", + }, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + "react/jsx-key": ["error", { checkFragmentShorthand: true }], + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + + // Relax promise rules given how we use them in this project + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-misused-promises": "off", + }, + }, + + { + files: ["src/**/__tests__/**"], + + rules: { + // Ideally, enable these lint rules again later, as they are useful to + // catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-floating-promises": "off", + }, + }, +]; diff --git a/packages/liveblocks-react-lexical/package.json b/packages/liveblocks-react-lexical/package.json index 2c60bb9947a..ab42b690388 100644 --- a/packages/liveblocks-react-lexical/package.json +++ b/packages/liveblocks-react-lexical/package.json @@ -79,7 +79,7 @@ "@liveblocks/vitest-config": "*", "@testing-library/jest-dom": "^5.16.5", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", diff --git a/packages/liveblocks-react-lexical/src/comments/wrap-selection-in-thread-mark-node.ts b/packages/liveblocks-react-lexical/src/comments/wrap-selection-in-thread-mark-node.ts index c6f81e775b8..0934fd225ee 100644 --- a/packages/liveblocks-react-lexical/src/comments/wrap-selection-in-thread-mark-node.ts +++ b/packages/liveblocks-react-lexical/src/comments/wrap-selection-in-thread-mark-node.ts @@ -124,7 +124,6 @@ export default function $wrapSelectionInThreadMarkNode( } // Make selection collapsed at the end if ($isElementNode(lastCreatedMarkNode)) { - // eslint-disable-next-line no-unused-expressions isBackward ? lastCreatedMarkNode.selectStart() : lastCreatedMarkNode.selectEnd(); diff --git a/packages/liveblocks-react-tiptap/.eslintrc.cjs b/packages/liveblocks-react-tiptap/.eslintrc.cjs deleted file mode 100644 index 958303bf03e..00000000000 --- a/packages/liveblocks-react-tiptap/.eslintrc.cjs +++ /dev/null @@ -1,76 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react", "react-hooks"], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - version: "detect", - }, - }, - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "react/jsx-key": ["error", { checkFragmentShorthand: true }], - "react/no-unescaped-entities": "error", - "react/no-unknown-property": "error", - - // Relax promise rules given how we use them in this project - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-misused-promises": "off", - }, - - overrides: [ - { - files: ["src/**/__tests__/**"], - - rules: { - // Ideally, enable these lint rules again later, as they are useful to - // catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-floating-promises": "off", - }, - }, - { - files: ["rollup.config.js"], - env: { - node: true, - }, - }, - ], -}; diff --git a/packages/liveblocks-react-tiptap/eslint.config.mjs b/packages/liveblocks-react-tiptap/eslint.config.mjs new file mode 100644 index 00000000000..0e45a93e9d4 --- /dev/null +++ b/packages/liveblocks-react-tiptap/eslint.config.mjs @@ -0,0 +1,73 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + react, + "react-hooks": reactHooks, + }, + + settings: { + react: { + version: "detect", + }, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + "react/jsx-key": ["error", { checkFragmentShorthand: true }], + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + + // Relax promise rules given how we use them in this project + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-misused-promises": "off", + }, + }, + + { + files: ["src/**/__tests__/**"], + + rules: { + // Ideally, enable these lint rules again later, as they are useful to + // catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-floating-promises": "off", + }, + }, +]; diff --git a/packages/liveblocks-react-tiptap/package.json b/packages/liveblocks-react-tiptap/package.json index fe30fabc8e1..02270290544 100644 --- a/packages/liveblocks-react-tiptap/package.json +++ b/packages/liveblocks-react-tiptap/package.json @@ -81,7 +81,7 @@ "@liveblocks/vitest-config": "*", "@testing-library/jest-dom": "^5.16.5", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", diff --git a/packages/liveblocks-react-tiptap/src/LiveblocksExtension.ts b/packages/liveblocks-react-tiptap/src/LiveblocksExtension.ts index 5460e5f9ecd..4c669afd17f 100644 --- a/packages/liveblocks-react-tiptap/src/LiveblocksExtension.ts +++ b/packages/liveblocks-react-tiptap/src/LiveblocksExtension.ts @@ -252,7 +252,6 @@ export const useLiveblocksExtension = ( const deleteTextMention = useDeleteTextMention(); // Tiptap has options default as any, in tiptap2, we could use never, but now we must use any - // eslint-disable-next-line @typescript-eslint/no-explicit-any return Extension.create({ name: "liveblocksExtension", diff --git a/packages/liveblocks-react-tiptap/src/ai/AiExtension.ts b/packages/liveblocks-react-tiptap/src/ai/AiExtension.ts index 2754a9518f3..c35416ee8dc 100644 --- a/packages/liveblocks-react-tiptap/src/ai/AiExtension.ts +++ b/packages/liveblocks-react-tiptap/src/ai/AiExtension.ts @@ -47,7 +47,6 @@ function getLiveblocksYjsProvider( editor: Editor ): LiveblocksYjsProvider | undefined { // Eslint doesn't seem to like Tiptap's Type declaration strategy - // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access return editor.extensionStorage.liveblocksExtension?.provider; } diff --git a/packages/liveblocks-react-tiptap/src/ai/AiToolbar.tsx b/packages/liveblocks-react-tiptap/src/ai/AiToolbar.tsx index 95bcf6b2491..7703e6695bf 100644 --- a/packages/liveblocks-react-tiptap/src/ai/AiToolbar.tsx +++ b/packages/liveblocks-react-tiptap/src/ai/AiToolbar.tsx @@ -301,7 +301,6 @@ function AiToolbarReviewingSuggestions() { function AiToolbarCustomPromptContent() { const editor = useCurrentEditor("CustomPromptContent", "AiToolbar"); // Eslint doesn't seem to like Tiptap's Type declaration strategy - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access const aiName = editor.storage.liveblocksAi.name; const textAreaRef = useRef(null); const { state, dropdownRef, isDropdownHidden } = useAiToolbarContext(); @@ -314,24 +313,18 @@ function AiToolbarCustomPromptContent() { [customPrompt] ); - useLayoutEffect( - () => { - requestAnimationFrame(() => { - const textArea = textAreaRef.current; + useLayoutEffect(() => { + requestAnimationFrame(() => { + const textArea = textAreaRef.current; - if (!textArea) { - return; - } + if (!textArea) { + return; + } - textArea.focus(); - textArea.setSelectionRange( - textArea.value.length, - textArea.value.length - ); - }); - }, - [] // eslint-disable-line react-hooks/exhaustive-deps - ); + textArea.focus(); + textArea.setSelectionRange(textArea.value.length, textArea.value.length); + }); + }, []); const handlePromptKeyDown = ( event: ReactKeyboardEvent diff --git a/packages/liveblocks-react-tiptap/src/collaboration/collaboration.ts b/packages/liveblocks-react-tiptap/src/collaboration/collaboration.ts index 4fdc8489896..ee9727b29bb 100644 --- a/packages/liveblocks-react-tiptap/src/collaboration/collaboration.ts +++ b/packages/liveblocks-react-tiptap/src/collaboration/collaboration.ts @@ -229,7 +229,6 @@ export const Collaboration = Extension.create< return { destroy: () => { const hasUndoManSelf = undoManager.trackedOrigins.has(undoManager); - // eslint-disable-next-line no-underscore-dangle const observers = undoManager._observers; if ( @@ -245,7 +244,6 @@ export const Collaboration = Extension.create< "afterTransaction", undoManager.afterTransactionHandler ); - // eslint-disable-next-line no-underscore-dangle undoManager._observers = observers; }; } diff --git a/packages/liveblocks-react-tiptap/src/collaboration/helpers/isChangeOrigin.ts b/packages/liveblocks-react-tiptap/src/collaboration/helpers/isChangeOrigin.ts index 6036966581d..0d832f92917 100644 --- a/packages/liveblocks-react-tiptap/src/collaboration/helpers/isChangeOrigin.ts +++ b/packages/liveblocks-react-tiptap/src/collaboration/helpers/isChangeOrigin.ts @@ -11,7 +11,7 @@ */ // There are some lint errors in this file as it's a direct copy of the original code from Tiptap. In order to not diverge from the original code, we're disabling the lint errors. -/* eslint-disable */ + import type { Transaction } from "@tiptap/pm/state"; import { ySyncPluginKey } from "y-prosemirror"; diff --git a/packages/liveblocks-react-ui/.eslintrc.cjs b/packages/liveblocks-react-ui/.eslintrc.cjs deleted file mode 100644 index 958303bf03e..00000000000 --- a/packages/liveblocks-react-ui/.eslintrc.cjs +++ /dev/null @@ -1,76 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react", "react-hooks"], - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - settings: { - react: { - version: "detect", - }, - }, - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - "react/jsx-key": ["error", { checkFragmentShorthand: true }], - "react/no-unescaped-entities": "error", - "react/no-unknown-property": "error", - - // Relax promise rules given how we use them in this project - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/no-misused-promises": "off", - }, - - overrides: [ - { - files: ["src/**/__tests__/**"], - - rules: { - // Ideally, enable these lint rules again later, as they are useful to - // catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-floating-promises": "off", - }, - }, - { - files: ["rollup.config.js"], - env: { - node: true, - }, - }, - ], -}; diff --git a/packages/liveblocks-react-ui/eslint.config.mjs b/packages/liveblocks-react-ui/eslint.config.mjs new file mode 100644 index 00000000000..0e45a93e9d4 --- /dev/null +++ b/packages/liveblocks-react-ui/eslint.config.mjs @@ -0,0 +1,73 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import react from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + react, + "react-hooks": reactHooks, + }, + + settings: { + react: { + version: "detect", + }, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + "react/jsx-key": ["error", { checkFragmentShorthand: true }], + "react/no-unescaped-entities": "error", + "react/no-unknown-property": "error", + + // Relax promise rules given how we use them in this project + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-misused-promises": "off", + }, + }, + + { + files: ["src/**/__tests__/**"], + + rules: { + // Ideally, enable these lint rules again later, as they are useful to + // catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-floating-promises": "off", + }, + }, +]; diff --git a/packages/liveblocks-react-ui/package.json b/packages/liveblocks-react-ui/package.json index b1e4d1eb721..4559dd65c93 100644 --- a/packages/liveblocks-react-ui/package.json +++ b/packages/liveblocks-react-ui/package.json @@ -110,7 +110,7 @@ "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "msw": "^2.10.4", "stylelint": "^15.10.2", "stylelint-config-standard": "^34.0.0", diff --git a/packages/liveblocks-react-ui/src/primitives/Duration.tsx b/packages/liveblocks-react-ui/src/primitives/Duration.tsx index 4ce5fd4d7d1..751eff6259b 100644 --- a/packages/liveblocks-react-ui/src/primitives/Duration.tsx +++ b/packages/liveblocks-react-ui/src/primitives/Duration.tsx @@ -328,7 +328,6 @@ export const Duration = forwardRef( typeof renderTitle === "function" ? renderTitle(resolvedDuration, locale) : renderTitle, - // eslint-disable-next-line react-hooks/exhaustive-deps [renderTitle, resolvedDuration, locale] ); const children = useMemo( diff --git a/packages/liveblocks-react/.eslintrc.cjs b/packages/liveblocks-react/.eslintrc.cjs deleted file mode 100644 index 65fd65bfa9d..00000000000 --- a/packages/liveblocks-react/.eslintrc.cjs +++ /dev/null @@ -1,60 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react-hooks"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": [ - "error", - ...commonRestrictedSyntax, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", - message: "use is only available on React >=19.", - }, - { - selector: - "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='useLayoutEffect']", - message: - "useLayoutEffect triggers a warning when executed on the server on React <=18.2.0. Import it from './lib/use-layout-effect' instead.", - }, - ], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - }, - - overrides: [ - { - files: ["src/**/__tests__/**"], - - rules: { - // Ideally, enable these lint rules again later, as they are useful to - // catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/unbound-method": "off", - "@typescript-eslint/no-floating-promises": "off", - "@typescript-eslint/require-await": "off", // Fine in test mocks - }, - }, - ], -}; diff --git a/packages/liveblocks-react/eslint.config.mjs b/packages/liveblocks-react/eslint.config.mjs new file mode 100644 index 00000000000..76339332adc --- /dev/null +++ b/packages/liveblocks-react/eslint.config.mjs @@ -0,0 +1,71 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + "react-hooks": reactHooks, + }, + + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": [ + "error", + ...commonRestrictedSyntax, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='use']", + message: "use is only available on React >=19.", + }, + { + selector: + "ImportDeclaration[source.value='react'] ImportSpecifier[imported.name='useLayoutEffect']", + message: + "useLayoutEffect triggers a warning when executed on the server on React <=18.2.0. Import it from './lib/use-layout-effect' instead.", + }, + ], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + + // TODO: New rules in typescript-eslint v8's recommendedTypeChecked + // that weren't in v7. Disabled during the v8 migration to keep it + // clean, but we should revisit and enable these later. + "@typescript-eslint/only-throw-error": "off", + "@typescript-eslint/prefer-promise-reject-errors": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + }, + }, + + { + files: ["src/**/__tests__/**"], + + rules: { + // Ideally, enable these lint rules again later, as they are useful to + // catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-return": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/require-await": "off", // Fine in test mocks + }, + }, +]; diff --git a/packages/liveblocks-react/package.json b/packages/liveblocks-react/package.json index dabdeddfc3c..ac44d6d3d58 100644 --- a/packages/liveblocks-react/package.json +++ b/packages/liveblocks-react/package.json @@ -86,7 +86,7 @@ "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.3.0", "date-fns": "^3.6.0", - "eslint-plugin-react-hooks": "^4.6.2", + "eslint-plugin-react-hooks": "^5.2.0", "itertools": "^2.3.2", "msw": "^2.10.4", "react-error-boundary": "^4.0.13" diff --git a/packages/liveblocks-react/src/lib/use-initial.ts b/packages/liveblocks-react/src/lib/use-initial.ts index fcb4214e2b8..6f977a132e5 100644 --- a/packages/liveblocks-react/src/lib/use-initial.ts +++ b/packages/liveblocks-react/src/lib/use-initial.ts @@ -27,7 +27,6 @@ export function useInitialUnlessFunction( type Fn = T & ((...args: unknown[]) => unknown); const ref = useLatest(latestValue as Fn); - // eslint-disable-next-line react-hooks/exhaustive-deps const wrapper = useCallback( ((...args: unknown[]) => ref.current(...args)) as Fn, [ref] diff --git a/packages/liveblocks-react/src/room.tsx b/packages/liveblocks-react/src/room.tsx index fa38054c009..76fcb670d48 100644 --- a/packages/liveblocks-react/src/room.tsx +++ b/packages/liveblocks-react/src/room.tsx @@ -4706,6 +4706,7 @@ function _useOthers( isEqual?: (prev: T, curr: T) => boolean ): T; function _useOthers(...args: any[]) { + // eslint-disable-next-line react-hooks/rules-of-hooks -- internal wrapper return useOthers(...(args as [])); } @@ -4766,6 +4767,7 @@ function _useOthersSuspense( isEqual?: (prev: T, curr: T) => boolean ): T; function _useOthersSuspense(...args: any[]) { + // eslint-disable-next-line react-hooks/rules-of-hooks -- internal wrapper return useOthersSuspense(...(args as [])); } @@ -4850,6 +4852,7 @@ function _useSelf( isEqual?: (prev: T, curr: T) => boolean ): T | null; function _useSelf(...args: any[]) { + // eslint-disable-next-line react-hooks/rules-of-hooks -- internal wrapper return useSelf(...(args as [])); } @@ -4885,6 +4888,7 @@ function _useSelfSuspense( isEqual?: (prev: T, curr: T) => boolean ): T; function _useSelfSuspense(...args: any[]) { + // eslint-disable-next-line react-hooks/rules-of-hooks -- internal wrapper return useSelfSuspense(...(args as [])); } diff --git a/packages/liveblocks-react/src/use-sync-external-store-with-selector.ts b/packages/liveblocks-react/src/use-sync-external-store-with-selector.ts index bd4f5e9ad0b..9404aa0f349 100644 --- a/packages/liveblocks-react/src/use-sync-external-store-with-selector.ts +++ b/packages/liveblocks-react/src/use-sync-external-store-with-selector.ts @@ -19,9 +19,7 @@ import { * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is */ function is(x: any, y: any) { - return ( - (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare - ); + return (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y); } // Same as useSyncExternalStore, but supports selector and isEqual arguments. diff --git a/packages/liveblocks-redux/.eslintrc.cjs b/packages/liveblocks-redux/.eslintrc.cjs deleted file mode 100644 index 44e6b3ca466..00000000000 --- a/packages/liveblocks-redux/.eslintrc.cjs +++ /dev/null @@ -1,32 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - eqeqeq: "off", - - // Ideally, enable these lint rules again later, as they are useful - // to catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - "@typescript-eslint/no-unsafe-call": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-unsafe-return": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - /* None yet 😇 ! */ - }, -}; diff --git a/packages/liveblocks-redux/eslint.config.mjs b/packages/liveblocks-redux/eslint.config.mjs new file mode 100644 index 00000000000..c2250d496b7 --- /dev/null +++ b/packages/liveblocks-redux/eslint.config.mjs @@ -0,0 +1,35 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + eqeqeq: "off", + + // Ideally, enable these lint rules again later, as they are useful + // to catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unsafe-return": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + /* None yet 😇 ! */ + }, + }, +]; diff --git a/packages/liveblocks-yjs/.eslintrc.cjs b/packages/liveblocks-yjs/.eslintrc.cjs deleted file mode 100644 index 2cbceeec907..00000000000 --- a/packages/liveblocks-yjs/.eslintrc.cjs +++ /dev/null @@ -1,43 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - eqeqeq: "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - /* None yet 😇 ! */ - }, - - overrides: [ - { - files: ["src/__tests__/**"], - - // Special config for test files - rules: { - "@typescript-eslint/explicit-module-boundary-types": "off", - - // Ideally, enable these lint rules again later, as they are useful - // to catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - // "@typescript-eslint/no-unsafe-call": "off", - // "@typescript-eslint/no-unsafe-member-access": "off", - // "@typescript-eslint/no-unsafe-return": "off", - }, - }, - ], -}; diff --git a/packages/liveblocks-yjs/eslint.config.mjs b/packages/liveblocks-yjs/eslint.config.mjs new file mode 100644 index 00000000000..aa3b7795a6f --- /dev/null +++ b/packages/liveblocks-yjs/eslint.config.mjs @@ -0,0 +1,44 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + eqeqeq: "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + /* None yet 😇 ! */ + }, + }, + + { + files: ["src/__tests__/**"], + + // Special config for test files + rules: { + "@typescript-eslint/explicit-module-boundary-types": "off", + + // Ideally, enable these lint rules again later, as they are useful + // to catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + // "@typescript-eslint/no-unsafe-call": "off", + // "@typescript-eslint/no-unsafe-member-access": "off", + // "@typescript-eslint/no-unsafe-return": "off", + }, + }, +]; diff --git a/packages/liveblocks-yjs/src/__tests__/index.test.ts b/packages/liveblocks-yjs/src/__tests__/index.test.ts index 7657f8abf8e..40807ce6302 100644 --- a/packages/liveblocks-yjs/src/__tests__/index.test.ts +++ b/packages/liveblocks-yjs/src/__tests__/index.test.ts @@ -1,6 +1,5 @@ import { describe, expect, test } from "vitest"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars import { LiveblocksYjsProvider } from ".."; describe("@liveblocks/yjs", () => { diff --git a/packages/liveblocks-yjs/src/awareness.ts b/packages/liveblocks-yjs/src/awareness.ts index 573a38a0fcb..2790bf7faa9 100644 --- a/packages/liveblocks-yjs/src/awareness.ts +++ b/packages/liveblocks-yjs/src/awareness.ts @@ -1,7 +1,3 @@ -// TODO: apparently Yjs is full of anys or something, see if we can fix this -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import type { BaseUserMeta, JsonObject, User } from "@liveblocks/client"; import type { OpaqueRoom } from "@liveblocks/core"; import { Observable } from "lib0/observable"; diff --git a/packages/liveblocks-zustand/.eslintrc.cjs b/packages/liveblocks-zustand/.eslintrc.cjs deleted file mode 100644 index aef6cc2bf02..00000000000 --- a/packages/liveblocks-zustand/.eslintrc.cjs +++ /dev/null @@ -1,41 +0,0 @@ -const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off", - eqeqeq: "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - /* None yet 😇 ! */ - }, - - overrides: [ - { - files: ["src/__tests__/**"], - - // Special config for test files - rules: { - // Ideally, enable these lint rules again later, as they are useful - // to catch bugs - "@typescript-eslint/no-unsafe-argument": "off", - "@typescript-eslint/no-unsafe-assignment": "off", - // "@typescript-eslint/no-unsafe-call": "off", - // "@typescript-eslint/no-unsafe-member-access": "off", - // "@typescript-eslint/no-unsafe-return": "off", - }, - }, - ], -}; diff --git a/packages/liveblocks-zustand/eslint.config.mjs b/packages/liveblocks-zustand/eslint.config.mjs new file mode 100644 index 00000000000..6cb7e7bd1ff --- /dev/null +++ b/packages/liveblocks-zustand/eslint.config.mjs @@ -0,0 +1,42 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import commonRestrictedSyntax from "@liveblocks/eslint-config/restricted-syntax"; + +export default [ + ...makeConfig(), + + { + rules: { + // ------------------------------- + // Custom syntax we want to forbid + // ------------------------------- + "no-restricted-syntax": ["error", ...commonRestrictedSyntax], + + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", + eqeqeq: "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + /* None yet 😇 ! */ + }, + }, + + { + files: ["src/__tests__/**"], + + // Special config for test files + rules: { + // Ideally, enable these lint rules again later, as they are useful + // to catch bugs + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + // "@typescript-eslint/no-unsafe-call": "off", + // "@typescript-eslint/no-unsafe-member-access": "off", + // "@typescript-eslint/no-unsafe-return": "off", + }, + }, +]; diff --git a/shared/eslint-config/index.js b/shared/eslint-config/index.js index 16fc6d9745f..ed11d67fb8a 100644 --- a/shared/eslint-config/index.js +++ b/shared/eslint-config/index.js @@ -1,65 +1,86 @@ -module.exports = { - parser: "@typescript-eslint/parser", - parserOptions: { - // Each project's individual/local tsconfig.json defines the behavior - // of the parser - project: ["./tsconfig.json"], - }, +import js from "@eslint/js"; +import importPlugin from "eslint-plugin-import"; +import simpleImportSort from "eslint-plugin-simple-import-sort"; +import tseslint from "typescript-eslint"; - plugins: [ - "@typescript-eslint", - "eslint-plugin-import", - "eslint-plugin-simple-import-sort", - ], +/** + * Creates the base ESLint flat config used by all packages in the monorepo. + * + * @param {string} tsconfigPath Path to the tsconfig.json (default: "./tsconfig.json") + * @returns {import("eslint").Linter.Config[]} + */ +export function makeConfig(tsconfigPath = "./tsconfig.json") { + return [ + js.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, - extends: [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - ], + { + languageOptions: { + parserOptions: { + // Each project's individual/local tsconfig.json defines the behavior + // of the parser + projectService: { + defaultProject: tsconfigPath, + }, + }, + }, - // Rules that are enabled for _all_ packages by default - rules: { - "@typescript-eslint/no-explicit-any": "error", + plugins: { + import: importPlugin, + "simple-import-sort": simpleImportSort, + }, - // ------------------------------- - // Not interested in these checks: - // ------------------------------- - "@typescript-eslint/no-empty-function": "off", - "@typescript-eslint/no-inferrable-types": "off", - "no-constant-condition": "off", - "@typescript-eslint/no-non-null-assertion": "off", // Because we have a custom no-restricted-syntax rule for this - "@typescript-eslint/no-base-to-string": "off", // Too many false positives for Yjs objects + // Rules that are enabled for _all_ packages by default + rules: { + "@typescript-eslint/no-explicit-any": "error", - // ----------------------------- - // Enable auto-fixes for imports - // ----------------------------- - "import/no-duplicates": "error", - "@typescript-eslint/consistent-type-imports": "error", - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", + // ------------------------------- + // Not interested in these checks: + // ------------------------------- + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-inferrable-types": "off", + "no-constant-condition": "off", + "@typescript-eslint/no-non-null-assertion": "off", // Because we have a custom no-restricted-syntax rule for this + "@typescript-eslint/no-base-to-string": "off", // Too many false positives for Yjs objects - // ------------------------ - // Customized default rules - // ------------------------ - eqeqeq: ["error", "always"], - quotes: [ - "error", - "double", - { avoidEscape: true, allowTemplateLiterals: false }, - ], - "object-shorthand": "error", - "@typescript-eslint/explicit-module-boundary-types": "error", - "@typescript-eslint/no-unused-vars": [ - "warn", - // Unused variables are fine if they start with an underscore - { args: "all", argsIgnorePattern: "^_.*", varsIgnorePattern: "^_.*" }, - ], + // ----------------------------- + // Enable auto-fixes for imports + // ----------------------------- + "import/no-duplicates": "error", + "@typescript-eslint/consistent-type-imports": "error", + "simple-import-sort/imports": "error", + "simple-import-sort/exports": "error", - // -------------------------------------------------------------- - // "The Code is the To-Do List" - // https://www.executeprogram.com/blog/the-code-is-the-to-do-list - // -------------------------------------------------------------- - "no-warning-comments": ["error", { terms: ["xxx"], location: "anywhere" }], - }, -}; + // ------------------------ + // Customized default rules + // ------------------------ + eqeqeq: ["error", "always"], + quotes: [ + "error", + "double", + { avoidEscape: true, allowTemplateLiterals: false }, + ], + "object-shorthand": "error", + "@typescript-eslint/explicit-module-boundary-types": "error", + "@typescript-eslint/no-unused-vars": [ + "warn", + // Unused variables are fine if they start with an underscore + { + args: "all", + argsIgnorePattern: "^_.*", + varsIgnorePattern: "^_.*", + }, + ], + + // -------------------------------------------------------------- + // "The Code is the To-Do List" + // https://www.executeprogram.com/blog/the-code-is-the-to-do-list + // -------------------------------------------------------------- + "no-warning-comments": [ + "error", + { terms: ["xxx"], location: "anywhere" }, + ], + }, + }, + ]; +} diff --git a/shared/eslint-config/package.json b/shared/eslint-config/package.json index 6d43799deea..c1c894669af 100644 --- a/shared/eslint-config/package.json +++ b/shared/eslint-config/package.json @@ -1,12 +1,16 @@ { "name": "@liveblocks/eslint-config", "private": true, - "main": "index.js", + "type": "module", + "exports": { + ".": "./index.js", + "./restricted-syntax": "./restricted-syntax.js" + }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.13.1", - "@typescript-eslint/parser": "^7.13.1", - "eslint": "^8.56.0", - "eslint-plugin-import": "^2.29.1", - "eslint-plugin-simple-import-sort": "^12.1.0" + "@eslint/js": "^9.39.4", + "eslint": "^9.39.4", + "eslint-plugin-import": "^2.32.0", + "eslint-plugin-simple-import-sort": "^13.0.0", + "typescript-eslint": "^8.58.2" } } diff --git a/shared/eslint-config/restricted-syntax.js b/shared/eslint-config/restricted-syntax.js index d9ea7a2580f..869a1f08c91 100644 --- a/shared/eslint-config/restricted-syntax.js +++ b/shared/eslint-config/restricted-syntax.js @@ -1,4 +1,4 @@ -module.exports = [ +export default [ { selector: 'TSTypeReference[typeName.name="AbstractCrdt"]', message: "Don't refer to AbstractCrdt as a type. Use LiveNode instead.", @@ -18,7 +18,6 @@ module.exports = [ { selector: "ImportDeclaration[source.value='vitest'] ImportSpecifier[imported.name='it']", - message: - "Import 'test' instead of 'it' from vitest.", + message: "Import 'test' instead of 'it' from vitest.", }, ]; diff --git a/tools/liveblocks-codemod/.eslintignore b/tools/liveblocks-codemod/.eslintignore deleted file mode 100644 index a0b51a6b990..00000000000 --- a/tools/liveblocks-codemod/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -__tests__ -__testfixtures__ diff --git a/tools/liveblocks-codemod/.eslintrc.cjs b/tools/liveblocks-codemod/.eslintrc.cjs deleted file mode 100644 index 4e836f04e80..00000000000 --- a/tools/liveblocks-codemod/.eslintrc.cjs +++ /dev/null @@ -1,14 +0,0 @@ -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - rules: { - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - /* None yet 😇 ! */ - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - "@typescript-eslint/explicit-module-boundary-types": "off", - }, -}; diff --git a/tools/liveblocks-codemod/eslint.config.mjs b/tools/liveblocks-codemod/eslint.config.mjs new file mode 100644 index 00000000000..dd88ca8d33c --- /dev/null +++ b/tools/liveblocks-codemod/eslint.config.mjs @@ -0,0 +1,18 @@ +import { makeConfig } from "@liveblocks/eslint-config"; + +export default [ + ...makeConfig(), + + { + ignores: ["**/__tests__/**", "**/__testfixtures__/**"], + }, + + { + rules: { + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/explicit-module-boundary-types": "off", + }, + }, +]; diff --git a/tools/liveblocks-codemod/src/lib/find-and-replace.ts b/tools/liveblocks-codemod/src/lib/find-and-replace.ts index 0d4b4f3cd7b..e8ec55966ed 100644 --- a/tools/liveblocks-codemod/src/lib/find-and-replace.ts +++ b/tools/liveblocks-codemod/src/lib/find-and-replace.ts @@ -17,7 +17,7 @@ export function findAndReplace( fs.writeFileSync(file, callback(content), { encoding: "utf-8" }); } - } catch (error) { + } catch { // console.error(error); } } diff --git a/tools/liveblocks-devtools/.eslintrc.cjs b/tools/liveblocks-devtools/.eslintrc.cjs deleted file mode 100644 index 52206b89326..00000000000 --- a/tools/liveblocks-devtools/.eslintrc.cjs +++ /dev/null @@ -1,27 +0,0 @@ -// const commonRestrictedSyntax = require("@liveblocks/eslint-config/restricted-syntax"); - -module.exports = { - root: true, - extends: ["@liveblocks/eslint-config"], - plugins: ["react-hooks"], - rules: { - // ------------------------------- - // Custom syntax we want to forbid - // ------------------------------- - // "no-restricted-syntax": ["error", ...commonRestrictedSyntax], - - // ---------------------------------------------------------------------- - // Overrides from default rule config used in all other projects! - // ---------------------------------------------------------------------- - "@typescript-eslint/explicit-module-boundary-types": "off", - "@typescript-eslint/unbound-method": "off", - - // ---------------------------------------------------------------------- - // Extra rules for this project specifically - // ---------------------------------------------------------------------- - - // Enforce React best practices - "react-hooks/rules-of-hooks": "error", - "react-hooks/exhaustive-deps": "error", - }, -}; diff --git a/tools/liveblocks-devtools/eslint.config.mjs b/tools/liveblocks-devtools/eslint.config.mjs new file mode 100644 index 00000000000..5552c525612 --- /dev/null +++ b/tools/liveblocks-devtools/eslint.config.mjs @@ -0,0 +1,36 @@ +import { makeConfig } from "@liveblocks/eslint-config"; +import reactHooks from "eslint-plugin-react-hooks"; + +export default [ + ...makeConfig(), + + { + plugins: { + "react-hooks": reactHooks, + }, + + rules: { + // ---------------------------------------------------------------------- + // Overrides from default rule config used in all other projects! + // ---------------------------------------------------------------------- + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-explicit-any": "off", + + // TODO: New rules in typescript-eslint v8's recommendedTypeChecked + // that weren't in v7. Disabled during the v8 migration to keep it + // clean, but we should revisit and enable these later. + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/restrict-template-expressions": "off", + + // ---------------------------------------------------------------------- + // Extra rules for this project specifically + // ---------------------------------------------------------------------- + + // Enforce React best practices + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "error", + }, + }, +]; diff --git a/tools/liveblocks-devtools/package.json b/tools/liveblocks-devtools/package.json index 870e3b96734..fe5f04dc30d 100644 --- a/tools/liveblocks-devtools/package.json +++ b/tools/liveblocks-devtools/package.json @@ -37,9 +37,10 @@ "yjs": "^13.6.14" }, "devDependencies": { + "@liveblocks/eslint-config": "*", "@types/webextension-polyfill": "^0.10.0", "autoprefixer": "^10.4.13", - "eslint-plugin-react-hooks": "^4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "postcss": "^8.4.20", "prettier-plugin-tailwindcss": "^0.5.4", "tailwindcss": "^3.2.4", diff --git a/tools/liveblocks-devtools/src/devtools/components/Dialog.tsx b/tools/liveblocks-devtools/src/devtools/components/Dialog.tsx index 552f53c886c..5889dd77288 100644 --- a/tools/liveblocks-devtools/src/devtools/components/Dialog.tsx +++ b/tools/liveblocks-devtools/src/devtools/components/Dialog.tsx @@ -4,7 +4,8 @@ import type { ReactNode } from "react"; import { forwardRef } from "react"; export interface Props - extends RadixDialog.DialogProps, + extends + RadixDialog.DialogProps, Omit { content: ReactNode; } diff --git a/tools/liveblocks-devtools/src/devtools/components/ResizablePanel.tsx b/tools/liveblocks-devtools/src/devtools/components/ResizablePanel.tsx index 69cf448e9ab..cf8eda53e9f 100644 --- a/tools/liveblocks-devtools/src/devtools/components/ResizablePanel.tsx +++ b/tools/liveblocks-devtools/src/devtools/components/ResizablePanel.tsx @@ -169,7 +169,6 @@ export function ResizablePanel({ , "content"> { +export interface SelectItem extends Omit< + ComponentProps, + "content" +> { value: string; content?: ReactNode; } diff --git a/tools/liveblocks-devtools/src/devtools/components/Tabs.tsx b/tools/liveblocks-devtools/src/devtools/components/Tabs.tsx index 2c8f8da3bd3..d466ad33485 100644 --- a/tools/liveblocks-devtools/src/devtools/components/Tabs.tsx +++ b/tools/liveblocks-devtools/src/devtools/components/Tabs.tsx @@ -4,8 +4,10 @@ import type { ComponentProps, ReactNode } from "react"; import { Tooltip } from "./Tooltip"; -export interface Tab - extends Omit, "content"> { +export interface Tab extends Omit< + ComponentProps, + "content" +> { value: string; title: string; richTitle?: ReactNode; diff --git a/tools/liveblocks-devtools/src/devtools/components/Tooltip.tsx b/tools/liveblocks-devtools/src/devtools/components/Tooltip.tsx index f5e65890ba6..efb06a6e685 100644 --- a/tools/liveblocks-devtools/src/devtools/components/Tooltip.tsx +++ b/tools/liveblocks-devtools/src/devtools/components/Tooltip.tsx @@ -3,7 +3,8 @@ import cx from "classnames"; import { forwardRef, type ReactNode } from "react"; export interface Props - extends RadixTooltip.TooltipProps, + extends + RadixTooltip.TooltipProps, Omit { content: ReactNode; } diff --git a/tools/liveblocks-devtools/src/devtools/components/Tree.tsx b/tools/liveblocks-devtools/src/devtools/components/Tree.tsx index 3e521327ed7..8b623ceec37 100644 --- a/tools/liveblocks-devtools/src/devtools/components/Tree.tsx +++ b/tools/liveblocks-devtools/src/devtools/components/Tree.tsx @@ -187,13 +187,15 @@ type TreeProps = Pick< > & ArboristTreeProps; -interface RowProps - extends ComponentProps<"div"> { +interface RowProps< + TTreeNode extends DevTools.TreeNode, +> extends ComponentProps<"div"> { node: NodeApi; } -interface YLogsRowProps - extends ComponentProps<"div"> { +interface YLogsRowProps< + TTreeNode extends YLogsTreeNode, +> extends ComponentProps<"div"> { node: NodeApi; } @@ -501,12 +503,14 @@ function summarize(node: DevTools.TreeNode): string { ); case "LiveList": - return `${node.payload.length} item${node.payload.length !== 1 ? "s" : "" - }`; + return `${node.payload.length} item${ + node.payload.length !== 1 ? "s" : "" + }`; case "LiveMap": - return `${node.payload.length} ${node.payload.length !== 1 ? "entries" : "entry" - }`; + return `${node.payload.length} ${ + node.payload.length !== 1 ? "entries" : "entry" + }`; case "User": return wrapObject( @@ -681,9 +685,9 @@ function Row({ "row text-dark-400 dark:text-light-400 group flex h-full items-center gap-2 pr-2", isSelected ? [ - background(node.data), - "tree-focus:text-light-0 bg-light-100 dark:bg-dark-100 hover:bg-light-200 dark:hover:bg-dark-200", - ] + background(node.data), + "tree-focus:text-light-0 bg-light-100 dark:bg-dark-100 hover:bg-light-200 dark:hover:bg-dark-200", + ] : isWithinSelectedParent ? "hover:bg-light-100 dark:hover:bg-dark-100 tree-focus:bg-light-100 dark:tree-focus:bg-dark-100 hover:tree-focus:bg-light-200 dark:tree-focus:hover:bg-dark-200" : "hover:bg-light-100 dark:hover:bg-dark-100" @@ -725,11 +729,11 @@ function Row({ className={cx( USE_GRID_LAYOUT ? [ - "grid min-w-0 flex-1 items-center gap-[inherit]", - isOpen - ? "grid-cols-[1fr]" - : "grid-cols-[minmax(0,1fr)_calc(var(--width)_*_0.4)]", - ] + "grid min-w-0 flex-1 items-center gap-[inherit]", + isOpen + ? "grid-cols-[1fr]" + : "grid-cols-[minmax(0,1fr)_calc(var(--width)_*_0.4)]", + ] : "flex min-w-0 flex-1 items-center gap-[inherit]" )} > @@ -757,9 +761,9 @@ function YLogsRow({ "row text-dark-400 dark:text-light-400 group flex h-full items-center gap-2 pr-2", isSelected ? [ - yLogsBackground(node.data), - "tree-focus:text-light-0 bg-light-100 dark:bg-dark-100 hover:bg-light-200 dark:hover:bg-dark-200", - ] + yLogsBackground(node.data), + "tree-focus:text-light-0 bg-light-100 dark:bg-dark-100 hover:bg-light-200 dark:hover:bg-dark-200", + ] : isWithinSelectedParent ? "hover:bg-light-100 dark:hover:bg-dark-100 tree-focus:bg-light-100 dark:tree-focus:bg-dark-100 hover:tree-focus:bg-light-200 dark:tree-focus:hover:bg-dark-200" : "hover:bg-light-100 dark:hover:bg-dark-100" @@ -789,11 +793,11 @@ function YLogsRow({ className={cx( USE_GRID_LAYOUT ? [ - "grid min-w-0 flex-1 items-center gap-[inherit]", - isOpen - ? "grid-cols-[1fr]" - : "grid-cols-[minmax(0,1fr)_calc(var(--width)_*_0.4)]", - ] + "grid min-w-0 flex-1 items-center gap-[inherit]", + isOpen + ? "grid-cols-[1fr]" + : "grid-cols-[minmax(0,1fr)_calc(var(--width)_*_0.4)]", + ] : "flex min-w-0 flex-1 items-center gap-[inherit]" )} > @@ -1585,16 +1589,16 @@ function presenceChildAccessor( value === undefined ? [] : [ - makeJsonNode( - `${node.id.substring(SPECIAL_HACK_PREFIX.length)}`, - // ^^^^^^^^^^^^^^^^^^^ - // Undo the "special behavior" for the - // subnodes, making them "normal Json" nodes - // that aren't expandable - key, - value - ), - ] + makeJsonNode( + `${node.id.substring(SPECIAL_HACK_PREFIX.length)}`, + // ^^^^^^^^^^^^^^^^^^^ + // Undo the "special behavior" for the + // subnodes, making them "normal Json" nodes + // that aren't expandable + key, + value + ), + ] ); } } @@ -1858,7 +1862,7 @@ function collectYNode( case "Y.Text": case "Y.XmlText": case "Y.XmlElement": - case "Y.XmlFragment": + case "Y.XmlFragment": { let isIndirectMatch = false; for (const childNode of node.payload) { if ( @@ -1871,6 +1875,7 @@ function collectYNode( indirectMatches.add(node.id); } return isIndirectMatch; + } } } } diff --git a/tools/liveblocks-devtools/src/devtools/contexts/CurrentRoom.tsx b/tools/liveblocks-devtools/src/devtools/contexts/CurrentRoom.tsx index f2afb2ed337..493da84972f 100644 --- a/tools/liveblocks-devtools/src/devtools/contexts/CurrentRoom.tsx +++ b/tools/liveblocks-devtools/src/devtools/contexts/CurrentRoom.tsx @@ -377,7 +377,7 @@ export function useCurrentRoomId(): string | null { } export function getRoom(roomId: string | null): Room | null { - return roomId ? roomsById.get(roomId) ?? null : null; + return roomId ? (roomsById.get(roomId) ?? null) : null; } export function useSetCurrentRoomId(): (roomId: string) => void { diff --git a/tools/liveblocks-devtools/src/devtools/index.tsx b/tools/liveblocks-devtools/src/devtools/index.tsx index 995bbdc8b0b..f6f04251a55 100644 --- a/tools/liveblocks-devtools/src/devtools/index.tsx +++ b/tools/liveblocks-devtools/src/devtools/index.tsx @@ -7,8 +7,8 @@ void browser.devtools.panels.create( "Liveblocks", "", // See: https://github.com/PlasmoHQ/plasmo/issues/106#issuecomment-1188539625 - // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call - liveblocksPanelHTML.split("/").pop()! + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call + liveblocksPanelHTML.split("/").pop() ); function Page() { diff --git a/tools/liveblocks-devtools/src/devtools/panel.tsx b/tools/liveblocks-devtools/src/devtools/panel.tsx index 20fe448ddf5..9a944bae6d5 100644 --- a/tools/liveblocks-devtools/src/devtools/panel.tsx +++ b/tools/liveblocks-devtools/src/devtools/panel.tsx @@ -263,5 +263,5 @@ function PanelApp() { ); } -const root = createRoot(document.getElementById("root")!); +const root = createRoot(document.getElementById("root")); root.render(); diff --git a/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-y-node.ts b/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-y-node.ts index 63b2fb1e194..4b39d909ca0 100644 --- a/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-y-node.ts +++ b/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-y-node.ts @@ -56,18 +56,18 @@ export type YContentBinaryNode = { export type YContentFormatNode = { type: "Y.ContentFormat"; data: { - [key: string]: Object; + [key: string]: object; }; }; export type YContentEmbedNode = { type: "Y.ContentEmbed"; - data: Object; + data: object; }; export type YContentAnyNode = { type: "Y.ContentAny"; - data: (Number | Object | Boolean | Array | String)[]; + data: (number | object | boolean | Array | string)[]; }; export type YAbstractTypeNode = diff --git a/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-yjs-tree-node.ts b/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-yjs-tree-node.ts index 4a344f10b57..6f10b2bef82 100644 --- a/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-yjs-tree-node.ts +++ b/tools/liveblocks-devtools/src/devtools/tabs/yjs/to-yjs-tree-node.ts @@ -53,7 +53,7 @@ export type YContentEmbedTreeNode = { type: YContentEmbedNode["type"]; id: string; key: string; - payload: Object; + payload: object; }; export type YContentBinaryTreeNode = { @@ -67,14 +67,14 @@ export type YContentFormatTreeNode = { type: YContentFormatNode["type"]; id: string; key: string; - payload: Object; + payload: object; }; export type YContentAnyTreeNode = { type: YContentAnyNode["type"]; id: string; key: string; - payload: (Number | Object | Boolean | Array | String)[]; + payload: (number | object | boolean | Array | string)[]; }; export type YContentTreeNode = diff --git a/tools/liveblocks-devtools/src/devtools/tabs/yjs/yflow/YItemNode.tsx b/tools/liveblocks-devtools/src/devtools/tabs/yjs/yflow/YItemNode.tsx index 5c8b4f2f185..4424e33bb7e 100644 --- a/tools/liveblocks-devtools/src/devtools/tabs/yjs/yflow/YItemNode.tsx +++ b/tools/liveblocks-devtools/src/devtools/tabs/yjs/yflow/YItemNode.tsx @@ -22,38 +22,28 @@ function YItemNode({ selected, data }: NodeProps) { switch (true) { case data.item.content instanceof Y.ContentDeleted: - component = ( - - ); + component = ; break; case data.item.content instanceof Y.ContentString: - component = ( - - ); + component = ; break; case data.item.content instanceof Y.ContentFormat: - component = ( - - ); + component = ; break; case data.item.content instanceof Y.ContentJSON: - component = ; + component = ; break; case data.item.content instanceof Y.ContentAny: - component = ; + component = ; break; case data.item.content instanceof Y.ContentType: - component = ; + component = ; break; case data.item.content instanceof Y.ContentBinary: - component = ( - - ); + component = ; break; case data.item.content instanceof Y.ContentEmbed: - component = ( - - ); + component = ; break; default: { component = ; diff --git a/tools/liveblocks-devtools/src/lib/stringify.ts b/tools/liveblocks-devtools/src/lib/stringify.ts index 94e1d9e1f6a..78c10010ebe 100644 --- a/tools/liveblocks-devtools/src/lib/stringify.ts +++ b/tools/liveblocks-devtools/src/lib/stringify.ts @@ -20,7 +20,7 @@ export function wrapObject(values?: string) { return values ? `{ ${values} }` : "{}"; } -type YjsContentData = Number | Object | Boolean | Array | String; +type YjsContentData = number | object | boolean | Array | string; export function stringify( value?: Json | YjsContentData | YjsContentData[], diff --git a/tools/liveblocks-devtools/src/lib/ydoc.ts b/tools/liveblocks-devtools/src/lib/ydoc.ts index 88dc38feddc..9309be002d1 100644 --- a/tools/liveblocks-devtools/src/lib/ydoc.ts +++ b/tools/liveblocks-devtools/src/lib/ydoc.ts @@ -1,4 +1,3 @@ -/* eslint-disable eqeqeq */ import type { Edge, Node } from "reactflow"; import { MarkerType } from "reactflow"; import * as Y from "yjs";