├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README-ZH.md ├── README.md ├── configs └── .eslintrc.js ├── docs └── rules │ └── demo.md ├── lib ├── index.js └── rules │ ├── demo.js │ └── interface-name-prefix.js ├── package.json ├── tests └── lib │ └── rules │ └── demo.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in 2 | # this file, please see the EditorConfig documentation: 3 | # https://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_size = 2 11 | indent_style = space 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = { 4 | root: true, 5 | env: { 6 | es2022: true, 7 | }, 8 | parserOptions: { 9 | ecmaVersion: "latest", 10 | sourceType: "module", 11 | }, 12 | plugins: ["unicorn"], 13 | extends: [ 14 | "eslint:recommended", 15 | "plugin:eslint-plugin/recommended", 16 | "plugin:node/recommended", 17 | "plugin:prettier/recommended", 18 | ], 19 | rules: { 20 | "node/no-unpublished-require": [ 21 | "error", 22 | { 23 | allowModules: ["requireindex"], 24 | }, 25 | ], 26 | }, 27 | overrides: [ 28 | { 29 | files: ["tests/**/*.js"], 30 | env: { mocha: true }, 31 | }, 32 | ], 33 | }; 34 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: [push, pull_request] 3 | 4 | env: 5 | CI: true 6 | VERSION: ${{ github.event.pull_request.number }} 7 | 8 | jobs: 9 | publish: 10 | name: Install and publish 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v3 15 | 16 | - name: Setup Node.js 17 | uses: actions/setup-node@v3 18 | with: 19 | node-version: 14.x 20 | 21 | - name: Restore yarn cache 22 | id: yarn-cache 23 | uses: actions/cache@v2 24 | with: 25 | path: "**/node_modules" 26 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 27 | 28 | - name: Install dependencies 29 | if: steps.yarn-cache.outputs.cache-hit != 'true' 30 | run: | 31 | yarn install --cwd example --frozen-lockfile 32 | yarn install --frozen-lockfile 33 | 34 | - name: Check eslint 35 | run: yarn lint 36 | 37 | - name: Check test 38 | run: yarn test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "dbaeumer.vscode-eslint", 5 | "EditorConfig.EditorConfig" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.quickSuggestions": { 3 | "strings": true 4 | }, 5 | "editor.formatOnSave": true, 6 | "editor.codeActionsOnSave": { 7 | "source.fixAll.eslint": true 8 | }, 9 | "editor.defaultFormatter": "esbenp.prettier-vscode", 10 | "prettier.requireConfig": true 11 | } 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Simon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README-ZH.md: -------------------------------------------------------------------------------- 1 | # 👨‍💻 @sj-distributor/eslint-plugin-react 2 | 3 | [![Npm Version](https://img.shields.io/npm/v/npm.svg)](https://www.npmjs.com/package/@sj-distributor/eslint-plugin-react) 4 | [![MIT License](https://img.shields.io/npm/l/react-native-tab-view.svg?style=flat-square)](https://www.npmjs.com/package/@sj-distributor/eslint-plugin-react) 5 | [![downloads](https://img.shields.io/npm/dm/eslint-config-standard.svg)](https://www.npmjs.com/package/@sj-distributor/eslint-plugin-react) 6 | [![Ci](https://github.com/sj-distributor/eslint-plugin-react/actions/workflows/ci.yml/badge.svg)](https://github.com/sj-distributor/eslint-plugin-react/actions/workflows/ci.yml) 7 | 8 | 用于 react 的 ESLint 预置 9 | 10 | ## 安装 11 | 12 | 你首先需要安装 [ESLint](https://eslint.org/) 和约定的第三方插件包: 13 | 14 | ```sh 15 | yarn add eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-import eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-simple-import-sort eslint-plugin-unicorn prettier typescript 16 | ``` 17 | 18 | 接下来,安装 `@sj-distributor/eslint-plugin-react`: 19 | 20 | ```sh 21 | yarn add @sj-distributor/eslint-plugin-react 22 | ``` 23 | 24 | ## 使用 25 | 26 | Add `eslint-plugin-react` to the extends section of your `.eslintrc` configuration file: 27 | 28 | ```json 29 | { 30 | "extends": ["plugin:@sj-distributor/react/recommended"] 31 | } 32 | ``` 33 | 34 | ## 使用了哪些规则? 35 | 36 | - [eslint](https://eslint.org/) 37 | - [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier#readme) 38 | - [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) 39 | - [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier#readme) 40 | - [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) 41 | - [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) 42 | - [eslint-plugin-simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort#readme) 43 | - [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint#readme) 44 | - [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint#readme) 45 | - [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn#readme) 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 👨‍💻 @sj-distributor/eslint-plugin-react 2 | 3 | [![Npm Version](https://img.shields.io/npm/v/npm.svg)](https://www.npmjs.com/package/@sj-distributor/eslint-plugin-react) 4 | [![MIT License](https://img.shields.io/npm/l/react-native-tab-view.svg?style=flat-square)](https://www.npmjs.com/package/@sj-distributor/eslint-plugin-react) 5 | [![downloads](https://img.shields.io/npm/dm/eslint-config-standard.svg)](https://www.npmjs.com/package/@sj-distributor/eslint-plugin-react) 6 | [![Ci](https://github.com/sj-distributor/eslint-plugin-react/actions/workflows/ci.yml/badge.svg)](https://github.com/sj-distributor/eslint-plugin-react/actions/workflows/ci.yml) 7 | 8 | ESLint presets for react 9 | 10 | ### [中文文档](https://github.com/sj-distributor/eslint-plugin-react/blob/master/README-ZH.md) 11 | 12 | ## Installation 13 | 14 | You first need to install [ESLint](https://eslint.org/) and the agreed third-party plug-in package: 15 | 16 | ```sh 17 | yarn add eslint @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-prettier eslint-plugin-import eslint-plugin-prettier eslint-plugin-react eslint-plugin-react-hooks eslint-plugin-simple-import-sort eslint-plugin-unicorn prettier typescript 18 | ``` 19 | 20 | Next, install `@sj-distributor/eslint-plugin-react`: 21 | 22 | ```sh 23 | yarn add @sj-distributor/eslint-plugin-react 24 | ``` 25 | 26 | ## Usage 27 | 28 | Add `eslint-plugin-react` to the extends section of your `.eslintrc` configuration file: 29 | 30 | ```json 31 | { 32 | "extends": ["plugin:@sj-distributor/react/recommended"] 33 | } 34 | ``` 35 | 36 | ## What rules are used? 37 | 38 | - [eslint](https://eslint.org/) 39 | - [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier#readme) 40 | - [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) 41 | - [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier#readme) 42 | - [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) 43 | - [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) 44 | - [eslint-plugin-simple-import-sort](https://github.com/lydell/eslint-plugin-simple-import-sort#readme) 45 | - [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint#readme) 46 | - [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint#readme) 47 | - [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn#readme) 48 | -------------------------------------------------------------------------------- /configs/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | "eslint:recommended", 5 | "plugin:react/recommended", 6 | "plugin:prettier/recommended", 7 | "plugin:react-hooks/recommended", 8 | "plugin:@typescript-eslint/recommended", 9 | ], 10 | parser: "@typescript-eslint/parser", 11 | plugins: [ 12 | "react", 13 | "simple-import-sort", 14 | "unicorn", 15 | "import", 16 | "@sj-distributor/react", 17 | ], 18 | env: { 19 | node: true, 20 | es2022: true, 21 | browser: true, 22 | }, 23 | parserOptions: { 24 | sourceType: "module", 25 | ecmaVersion: "latest", 26 | ecmaFeatures: { 27 | jsx: true, 28 | }, 29 | }, 30 | rules: { 31 | camelcase: "error", // 要求变量命名使用驼峰式 32 | "spaced-comment": "error", // 要求强制执行注释 // 或 /* 开始后的间距的一致性 33 | "import/extensions": 0, // (关闭) .js 解析为模块并检查 exports 的文件扩展名列表,如果是React 或者使用 TypeScript 将要去共享配置 settings 添加修改 34 | "import/no-unresolved": 0, // (关闭) 导入的模块可以解析为本地文件系统上的模块 35 | "no-duplicate-imports": "error", // 要求每个模块只能有一个 imports,相同的内容可以在各自的 imports 上看到导入的内容 36 | "react/react-in-jsx-scope": 0, // (关闭) 使用 jsx 时不允许缺少 React 的引入 37 | "simple-import-sort/imports": "error", // imports 排序 38 | "simple-import-sort/exports": "error", // exports 排序 39 | // 强制文件名大小写样式 40 | "unicorn/filename-case": [ 41 | "error", 42 | { 43 | cases: { 44 | kebabCase: true, // 支持横杠 (-) 命名 45 | camelCase: false, // 支持小驼峰命名 46 | snakeCase: false, // 支持 (_) 下划线命名 47 | pascalCase: false, // 支持大坨峰命名 48 | }, 49 | }, 50 | ], 51 | // Hooks 有依赖项的话强制要求补充依赖项 52 | "react-hooks/rules-of-hooks": "error", 53 | "react-hooks/exhaustive-deps": ["error"], 54 | // 要求或不允许在给定的2种语句之间有空行 55 | "padding-line-between-statements": [ 56 | "error", 57 | { blankLine: "always", prev: "*", next: "return" }, // 要求 return 语句之前要有空行 58 | { 59 | blankLine: "always", 60 | prev: ["function", "const", "var", "let"], // 指定语法糖语句之间要有空行 61 | next: "*", 62 | }, 63 | ], 64 | "@typescript-eslint/no-var-requires": 0, // (关闭) 禁止使用 require 语句 65 | "@sj-distributor/react/interface-name-prefix": ["error", "I"], // 默认强制 interface 大写 I 前缀 66 | "react/display-name": 0, // (关闭) 不允许在 React 组件定义中缺少 displayName 67 | "react/self-closing-comp": 2, // 检测 JSX 中的所有组件和 HTML 元素,如果元素没有子元素,就会自动转换为自闭合形式 68 | }, 69 | // 共享配置,提供给每一个将被执行的规则 70 | settings: { 71 | "import/resolver": { 72 | node: { 73 | extensions: [".js", ".jsx", ".ts", ".tsx"], 74 | }, 75 | }, 76 | react: { 77 | version: "detect", // ‘detect’ 会自动选择你所安装的 React 版本 78 | }, 79 | }, 80 | }; 81 | -------------------------------------------------------------------------------- /docs/rules/demo.md: -------------------------------------------------------------------------------- 1 | # demo example (demo) 2 | 3 | Please describe the origin of the rule here. 4 | 5 | ## Rule Details 6 | 7 | This rule aims to... 8 | 9 | Examples of **incorrect** code for this rule: 10 | 11 | ```js 12 | // fill me in 13 | ``` 14 | 15 | Examples of **correct** code for this rule: 16 | 17 | ```js 18 | // fill me in 19 | ``` 20 | 21 | ### Options 22 | 23 | If there are any options, describe them here. Otherwise, delete this section. 24 | 25 | ## When Not To Use It 26 | 27 | Give a short description of when it would be appropriate to turn off this rule. 28 | 29 | ## Further Reading 30 | 31 | If there are other links that describe the issue this rule addresses, please include them here in a bulleted list. 32 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview ESLint presets for react 3 | */ 4 | "use strict"; 5 | 6 | //------------------------------------------------------------------------------ 7 | // Requirements 8 | //------------------------------------------------------------------------------ 9 | 10 | const requireIndex = require("requireindex"); 11 | const path = require("path"); 12 | const eslintrc = require("../configs/.eslintrc"); 13 | 14 | //------------------------------------------------------------------------------ 15 | // Plugin Definition 16 | //------------------------------------------------------------------------------ 17 | module.exports = { 18 | // 引入所有的自定义的规则 19 | rules: requireIndex(path.join(__dirname + "/rules")), 20 | configs: { 21 | recommended: eslintrc, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /lib/rules/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview demo example 3 | */ 4 | "use strict"; 5 | 6 | //------------------------------------------------------------------------------ 7 | // Rule Definition 8 | //------------------------------------------------------------------------------ 9 | 10 | /** @type {import('eslint').Rule.RuleModule} */ 11 | module.exports = { 12 | meta: { 13 | messages: "demo example", 14 | type: "suggestion", // `problem`, `suggestion`, or `layout` 15 | docs: { 16 | description: "demo example", 17 | recommended: false, 18 | url: null, // URL to the documentation page for this rule 19 | }, 20 | fixable: null, // Or `code` or `whitespace` 21 | schema: [], // Add a schema if the rule has options 22 | }, 23 | // eslint-disable-next-line no-unused-vars 24 | create(context) { 25 | // variables should be defined here 26 | 27 | //---------------------------------------------------------------------- 28 | // Helpers 29 | //---------------------------------------------------------------------- 30 | 31 | // any helper functions should go here or else delete this section 32 | 33 | //---------------------------------------------------------------------- 34 | // Public 35 | //---------------------------------------------------------------------- 36 | 37 | return { 38 | // visitor functions for different types of nodes 39 | }; 40 | }, 41 | }; 42 | -------------------------------------------------------------------------------- /lib/rules/interface-name-prefix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Require interface names to begin with a specified prefix 3 | */ 4 | "use strict"; 5 | 6 | //------------------------------------------------------------------------------ 7 | // Rule Definition 8 | //------------------------------------------------------------------------------ 9 | 10 | /** @type {import('eslint').Rule.RuleModule} */ 11 | module.exports = { 12 | meta: { 13 | type: "suggestion", 14 | docs: { 15 | category: "Naming", 16 | recommended: false, 17 | description: "Require interface names to begin with a specified prefix", 18 | }, 19 | fixable: "code", 20 | schema: [ 21 | { 22 | type: "string", 23 | }, 24 | ], 25 | messages: { 26 | missingPrefix: 27 | "Interface name '{{ name }}' should start with '{{ prefix }}'", 28 | }, 29 | }, 30 | 31 | create(context) { 32 | const [prefix = "I"] = context.options; 33 | 34 | function checkPrefix(node) { 35 | const name = node.id.name; 36 | 37 | if ( 38 | typeof name !== "string" || 39 | name.startsWith(prefix) || 40 | node.parent.type === "TSModuleDeclaration" 41 | ) { 42 | return; 43 | } 44 | 45 | context.report({ 46 | node, 47 | messageId: "missingPrefix", 48 | data: { 49 | name, 50 | prefix, 51 | }, 52 | fix: (fixer) => { 53 | const newName = `${prefix}${name}`; 54 | 55 | return fixer.replaceText(node.id, newName); 56 | }, 57 | }); 58 | } 59 | 60 | return { 61 | TSInterfaceDeclaration: checkPrefix, 62 | }; 63 | }, 64 | }; 65 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@sj-distributor/eslint-plugin-react", 3 | "version": "0.7.1", 4 | "description": "ESLint presets for react", 5 | "keywords": [ 6 | "eslint", 7 | "eslintplugin", 8 | "eslint-plugin" 9 | ], 10 | "main": "./lib/index.js", 11 | "exports": "./lib/index.js", 12 | "scripts": { 13 | "lint": "eslint .", 14 | "release": "release-it --no-increment", 15 | "test": "mocha tests --recursive" 16 | }, 17 | "dependencies": { 18 | "requireindex": "^1.2.0" 19 | }, 20 | "devDependencies": { 21 | "@commitlint/config-conventional": "^17.1.0", 22 | "@release-it/conventional-changelog": "^5.1.1", 23 | "@typescript-eslint/eslint-plugin": "^5.40.1", 24 | "@typescript-eslint/parser": "^5.40.1", 25 | "eslint": "^8.19.0", 26 | "eslint-config-prettier": "^8.5.0", 27 | "eslint-plugin-eslint-plugin": "^5.0.0", 28 | "eslint-plugin-import": "^2.26.0", 29 | "eslint-plugin-node": "^11.1.0", 30 | "eslint-plugin-prettier": "^4.2.1", 31 | "eslint-plugin-react": "^7.31.10", 32 | "eslint-plugin-react-hooks": "^4.6.0", 33 | "eslint-plugin-simple-import-sort": "^8.0.0", 34 | "eslint-plugin-unicorn": "^42.0.0", 35 | "prettier": "^2.6.2", 36 | "mocha": "^10.0.0", 37 | "release-it": "^15.5.0", 38 | "typescript": "^4.8.4" 39 | }, 40 | "engines": { 41 | "node": "^14.17.0 || ^16.0.0 || >= 18.0.0" 42 | }, 43 | "peerDependencies": { 44 | "eslint": ">=7" 45 | }, 46 | "license": "MIT", 47 | "repository": "https://github.com/sj-distributor/eslint-plugin-react", 48 | "bugs": { 49 | "url": "https://github.com/sj-distributor/eslint-plugin-react/issues" 50 | }, 51 | "homepage": "https://github.com/sj-distributor/eslint-plugin-react#readme", 52 | "publishConfig": { 53 | "access": "public", 54 | "registry": "https://registry.npmjs.org/" 55 | }, 56 | "release-it": { 57 | "git": { 58 | "commitMessage": "chore: release ${version}", 59 | "tagName": "v${version}" 60 | }, 61 | "npm": { 62 | "publish": true 63 | }, 64 | "github": { 65 | "release": true 66 | }, 67 | "plugins": { 68 | "@release-it/conventional-changelog": { 69 | "preset": "angular" 70 | } 71 | } 72 | }, 73 | "commitlint": { 74 | "extends": [ 75 | "@commitlint/config-conventional" 76 | ] 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tests/lib/rules/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview demo example 3 | */ 4 | "use strict"; 5 | 6 | //------------------------------------------------------------------------------ 7 | // Requirements 8 | //------------------------------------------------------------------------------ 9 | 10 | // const rule = require("../../../lib/rules/demo"), 11 | // RuleTester = require("eslint").RuleTester; 12 | 13 | //------------------------------------------------------------------------------ 14 | // Tests 15 | //------------------------------------------------------------------------------ 16 | 17 | // const ruleTester = new RuleTester(); 18 | // ruleTester.run("demo", rule, { 19 | // valid: [ 20 | // // give me some code that won't trigger a warning 21 | // ], 22 | 23 | // invalid: [ 24 | // { 25 | // code: "demo example", 26 | // errors: [{ message: "Fill me in.", type: "Me too" }], 27 | // }, 28 | // ], 29 | // }); 30 | --------------------------------------------------------------------------------