diff --git a/package-lock.json b/package-lock.json index 80211c7..bd3cd6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1675,6 +1675,15 @@ "node": ">= 8" } }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, "node_modules/@npmcli/agent": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", @@ -2622,6 +2631,10 @@ "resolved": "packages/eslint-config-react", "link": true }, + "node_modules/@strv/eslint-config-react-native": { + "resolved": "packages/eslint-config-react-native", + "link": true + }, "node_modules/@strv/eslint-config-typescript": { "resolved": "packages/eslint-config-typescript", "link": true @@ -6861,29 +6874,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -7223,6 +7213,71 @@ "eslint": ">=6.0.0" } }, + "node_modules/eslint-config-expo": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-expo/-/eslint-config-expo-10.0.0.tgz", + "integrity": "sha512-/XC/DvniUWTzU7Ypb/cLDhDD4DXqEio4lug1ObD/oQ9Hcx3OVOR8Mkp4u6U4iGoZSJyIQmIk3WVHe/P1NYUXKw==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "^8.18.2", + "@typescript-eslint/parser": "^8.18.2", + "eslint-import-resolver-typescript": "^3.6.3", + "eslint-plugin-expo": "^1.0.0", + "eslint-plugin-import": "^2.30.0", + "eslint-plugin-react": "^7.37.3", + "eslint-plugin-react-hooks": "^5.1.0", + "globals": "^16.0.0" + }, + "peerDependencies": { + "eslint": ">=8.10" + } + }, + "node_modules/eslint-config-expo/node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-config-expo/node_modules/globals": { + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint-import-context": { "version": "0.1.6", "resolved": "https://registry.npmjs.org/eslint-import-context/-/eslint-import-context-0.1.6.tgz", @@ -7348,6 +7403,23 @@ "eslint": ">=8" } }, + "node_modules/eslint-plugin-expo": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-expo/-/eslint-plugin-expo-1.0.0.tgz", + "integrity": "sha512-qLtunR+cNFtC+jwYCBia5c/PJurMjSLMOV78KrEOyQK02ohZapU4dCFFnS2hfrJuw0zxfsjVkjqg3QBqi933QA==", + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "^8.29.1", + "@typescript-eslint/utils": "^8.29.1", + "eslint": "^9.24.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "eslint": ">=8.10" + } + }, "node_modules/eslint-plugin-graphql": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-4.0.0.tgz", @@ -21173,6 +21245,18 @@ "eslint": "^9" } }, + "packages/eslint-config-react-native": { + "name": "@strv/eslint-config-react-native", + "version": "4.0.0", + "license": "BSD-3-Clause", + "dependencies": { + "@strv/eslint-config-typescript": "^6.0.0", + "eslint-config-expo": "^10.0.0" + }, + "peerDependencies": { + "eslint": "^9" + } + }, "packages/eslint-config-typescript": { "name": "@strv/eslint-config-typescript", "version": "6.0.0", diff --git a/package.json b/package.json index f6ef1bb..faad748 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "node": ">=24", "npm": ">=11" }, + "packageManager": "npm@11.6.2", "type": "module", "homepage": "https://github.com/strvcom/code-quality-tools", "keywords": [], diff --git a/packages/eslint-config-react-native/LICENSE b/packages/eslint-config-react-native/LICENSE new file mode 100644 index 0000000..d310f30 --- /dev/null +++ b/packages/eslint-config-react-native/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2019, STRV +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/eslint-config-react-native/README.md b/packages/eslint-config-react-native/README.md new file mode 100644 index 0000000..a30947f --- /dev/null +++ b/packages/eslint-config-react-native/README.md @@ -0,0 +1,25 @@ +# @strv/eslint-config-react-native + +> ESLint config for React-Native projects + +## Installation + +```sh +npm install -D @strv/eslint-config-react-native +``` + +## Usage + +```js +// eslint.config.mjs +import reactNative from '@strv/eslint-config-react-native' + +export default reactNative +``` + +### Tips +1. Use caching, see https://eslint.org/docs/latest/use/command-line-interface#caching + +## License + +See the [LICENSE](LICENSE) file for information. diff --git a/packages/eslint-config-react-native/index.mjs b/packages/eslint-config-react-native/index.mjs new file mode 100644 index 0000000..796bed6 --- /dev/null +++ b/packages/eslint-config-react-native/index.mjs @@ -0,0 +1,88 @@ +import typeScriptConfig from '@strv/eslint-config-typescript' +import expoConfig from 'eslint-config-expo/flat.js' +import { defineConfig, globalIgnores } from 'eslint/config' + +/** Globally ignored */ +const ignores = globalIgnores(['.expo/', 'expo-env.d.ts']) + +/** @type {import("eslint").Linter.Config} */ +const common = { + rules: { + // Very expensive check, see https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/namespace.md + 'import/namespace': 'off', + // Very expensive check, see https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-duplicates.md + 'import/no-duplicates': 'off', + // Handled by TypeScript, see https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-unresolved.md + 'import/no-unresolved': 'off', + // https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-named-as-default.md + 'import/no-named-as-default': 'off', + // https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-named-as-default-member.md + 'import/no-named-as-default-member': 'off', + // Handled by TypeScript. Enable noUnusedLocals in your tsconfig.json, see https://www.typescriptlang.org/tsconfig/#noUnusedLocals + // https://eslint.org/docs/latest/rules/no-unused-vars + 'no-unused-vars': 'off', + }, +} + +/** @type {import("eslint").Linter.Config} */ +const react = { + files: ['**/*.jsx', '**/*.tsx'], + rules: { + // Enforce alphabetical sorting of props for better readability, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md + 'react/jsx-sort-props': [ + 'error', + { + multiline: 'first', + reservedFirst: ['key'], + callbacksLast: true, + shorthandLast: true, + }, + ], + // DisplayName is not required for React Native components, see https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/display-name.md + 'react/display-name': 'off', + }, +} + +/** @type {import("eslint").Linter.Config} */ +const typescript = defineConfig([typeScriptConfig, { + files: ['**/*.ts', '**/*.tsx'], + rules: { + // Handled by TypeScript. Enable noUnusedLocals in your tsconfig.json, see https://www.typescriptlang.org/tsconfig/#noUnusedLocals + // https://typescript-eslint.io/rules/no-unused-vars/ + '@typescript-eslint/no-unused-vars': 'off', + // Its common in React Native to import types using require syntax, see https://reactnative.dev/docs/images#static-image-resources + // https://typescript-eslint.io/rules/no-require-imports/ + '@typescript-eslint/no-require-imports': 'off', + // Very expensive check, see https://typescript-eslint.io/rules/promise-function-async/ + '@typescript-eslint/promise-function-async': 'off', + + // Allows variable shadowing in TypeScript contexts, see https://typescript-eslint.io/rules/no-shadow/ + '@typescript-eslint/no-shadow': 'off', + // Allows both interface and type definitions, see https://typescript-eslint.io/rules/consistent-type-definitions/ + '@typescript-eslint/consistent-type-definitions': 'off', + // Allows Promises in places where they might not be handled properly, see https://typescript-eslint.io/rules/no-misused-promises/ + '@typescript-eslint/no-misused-promises': 'off', + // Allows assignments of any typed values, see https://typescript-eslint.io/rules/no-unsafe-assignment/ + '@typescript-eslint/no-unsafe-assignment': 'off', + // Allows returning any typed values from functions, see https://typescript-eslint.io/rules/no-unsafe-return/ + '@typescript-eslint/no-unsafe-return': 'off', + // Allows unbound method references that may lose 'this' context, see https://typescript-eslint.io/rules/unbound-method/ + '@typescript-eslint/unbound-method': 'off', + // Allows member access on any typed values, see https://typescript-eslint.io/rules/no-unsafe-member-access/ + '@typescript-eslint/no-unsafe-member-access': 'off', + + // Enforce consistent type imports with inline style, see https://typescript-eslint.io/rules/consistent-type-imports/ + '@typescript-eslint/consistent-type-imports': [ + 'error', + { prefer: 'type-imports', fixStyle: 'inline-type-imports' }, + ], + }, +}]) + +export default defineConfig([ + expoConfig, + ignores, + common, + typescript, + react, +]) diff --git a/packages/eslint-config-react-native/package.json b/packages/eslint-config-react-native/package.json new file mode 100644 index 0000000..46e69cb --- /dev/null +++ b/packages/eslint-config-react-native/package.json @@ -0,0 +1,28 @@ +{ + "name": "@strv/eslint-config-react-native", + "description": "STRV's ESLint config for React Native projects", + "version": "4.0.0", + "author": "Petr Chalupa ", + "keywords": ["eslint", "react-native", "config", "strv"], + "repository": { + "type": "git", + "url": "git://github.com/strvcom/code-quality-tools.git" + }, + "bugs": { + "url": "https://github.com/strvcom/code-quality-tools/issues" + }, + "homepage": "https://github.com/strvcom/code-quality-tools/tree/master/packages/eslint-config-react-native#readme", + "license": "BSD-3-Clause", + "main": "index.mjs", + "type": "module", + "dependencies": { + "@strv/eslint-config-typescript": "^6.0.0", + "eslint-config-expo": "^10.0.0" + }, + "peerDependencies": { + "eslint": "^9" + }, + "publishConfig": { + "access": "public" + } +}