Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create @mediamonks/eslint-plugin-react #109

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@ node_modules/

# WebStorms .idea folder
.idea

# Build directories
dist/
5 changes: 5 additions & 0 deletions lint-staged.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const scriptExtensionsGlob = '?(m|c){j,t}s?x';

export default {
'**/*': () => 'prettier . --write --loglevel=warn',
};
46,380 changes: 32,288 additions & 14,092 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"eslint": "npm run eslint --workspaces --if-present",
"eslint:fix": "npm run eslint:fix --workspaces --if-present",
"build": "npm run build --workspaces --if-present",
"format": "prettier --write \"./**/*.{ts,js,tsx,jsx,json}\"",
"prepare": "husky install"
"format": "prettier . --write --loglevel=warn",
"postinstall": "husky install"
},
"devDependencies": {
"@mediamonks/eslint-config": "*",
Expand All @@ -28,15 +28,13 @@
"prettier": "@mediamonks/prettier-config",
"eslintConfig": {
"extends": "@mediamonks/eslint-config",
"root": true,
"env": {
"node": true,
"browser": true
},
"parserOptions": {
"ecmaVersion": "latest"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx,md,json}": "prettier --write"
}
}
1 change: 1 addition & 0 deletions packages/eslint-config-react/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module.exports = {
'plugin:react-hooks/recommended',
'plugin:react/jsx-runtime',
'plugin:jsx-a11y/recommended',
'plugin:@mediamonks/react/recommended',
],
settings: {
react: {
Expand Down
1 change: 1 addition & 0 deletions packages/eslint-config-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
],
"main": "index.js",
"dependencies": {
"@mediamonks/eslint-plugin-react": "1.0.0-1",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0"
Expand Down
45 changes: 45 additions & 0 deletions packages/eslint-plugin-react/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Media.Monks - @mediamonks/eslint-plugin-react

## Installation

Install [ESLint](https://eslint.org/) and `@mediamonks/eslint-plugin-react`:

```sh
npm install --save-dev eslint @mediamonks/eslint-plugin-react
```

## Usage

Configure the rules you want to use under the rules section.

```json
{
"plugins": ["plugin:@mediamonks/react"],
"rules": {
"@mediamonks/react/rule-name": 2
}
}
```

Or use the recommended config

```json
{
"extends": ["plugin:@mediamonks/react/recommended"]
}
```

## Rules

<!-- begin auto-generated rules list -->

💼 Configurations enabled in.\
✅ Set in the `recommended` configuration.\
💡 Manually fixable by
[editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).

| Name | Description | 💼 | 💡 |
| :------------------------------------------------------------- | :------------------------------------ | :-- | :-- |
| [throttle-hook-callback](docs/rules/throttle-hook-callback.md) | Callback in hook should be throttled. | ✅ | 💡 |

<!-- end auto-generated rules list -->
75 changes: 75 additions & 0 deletions packages/eslint-plugin-react/docs/rules/throttle-hook-callback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Callback in hook should be throttled (`@mediamonks/react/throttle-hook-callback`)

💼 This rule is enabled in the ✅ `recommended` config.

💡 This rule is manually fixable by
[editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).

<!-- end auto-generated rule header -->

## Rule details

This rule enforces that the callback in a hook should be wrapped with a throttle function. By
default, the rule allows the following throttle functions:

- `useRafCallback`
- `useDebounceCallback`
- `useThrottleCallback`
- `throttle`
- `debounce`

## Examples

### Invalid

```js
useResizeObserver(elementRef, () => {
// Resize logic
});
```

### Valid

```js
useResizeObserver(
elementRef,
useRafCallback(() => {
// Resize logic
}),
);
```

## Options

This rule has an optional object configuration:

- `hookNames`: An array of strings specifying custom hook names. Default hook names will be used if
this option is not provided.
- `throttleFunctionNames`: An array of strings specifying custom throttle function names. Default
throttle function names will be used if this option is not provided.

## Usage

This rule takes one optional object argument of type object:

```json
{
"rules": {
"@mediamonks/react/use-resize-observer-throttle-callback": [
"error",
{
"hookNames": ["useCustomHook"],
"throttleFunctionNames": ["customThrottleFunction"]
}
]
}
}
```

## Suggestions

This rule provides suggestions to automatically wrap the callback function with a throttle function:

1. Wrap with `useRafCallback`
2. Wrap with `useDebounceCallback`
3. Wrap with `useThrottleCallback`
20 changes: 20 additions & 0 deletions packages/eslint-plugin-react/index.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { type ESLint } from 'eslint';
import * as throttleCallback from './rules/throttleHookCallback';

export = {
rules: {
[throttleCallback.name]: throttleCallback.default,
},
configs: {
recommended: {
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@mediamonks/react'],
rules: {
[`@mediamonks/react/${throttleCallback.name}`]: 'error',
},
},
},
} satisfies ESLint.Plugin;
6 changes: 6 additions & 0 deletions packages/eslint-plugin-react/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { type Config } from 'jest';

export default {
preset: 'ts-jest',
testEnvironment: 'node',
} satisfies Config;
5 changes: 5 additions & 0 deletions packages/eslint-plugin-react/lib/getRuleDocumentationPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { repository } from '../package.json';

export function getRuleDocumentationPath(ruleName: string): string {
return `${repository.url.slice(0, -4)}/${repository.directory}/docs/rules/${ruleName}.md`;
}
65 changes: 65 additions & 0 deletions packages/eslint-plugin-react/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "@mediamonks/eslint-plugin-react",
"version": "1.0.0-1",
"publishConfig": {
"access": "public"
},
"license": "MIT",
"author": "frontend.monks",
"homepage": "https://github.com/mediamonks/eslint-config#readme",
"bugs": {
"url": "https://github.com/mediamonks/eslint-config/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/mediamonks/eslint-config.git",
"directory": "packages/eslint-plugin-react"
},
"description": "Sharable eslint config based on Media.Monks Frontend Coding Standards",
"keywords": [
"Media.Monks",
"eslint",
"eslintplugin",
"eslint-config"
],
"main": "./dist/index.cjs",
"scripts": {
"lint": "npm-run-all \"lint:*\"",
"lint:js": "eslint .",
"lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"",
"update:eslint-docs": "eslint-doc-generator",
"build": "rm -rf ./dist && tsc --build tsconfig.json",
"test": "jest ./**/*.test.ts"
},
"dependencies": {
"@types/eslint": "^8.37.0",
"generator-eslint": "^4.1.3"
},
"peerDependencies": {
"eslint": ">=8.34.0"
},
"devDependencies": {
"eslint-doc-generator": "^1.6.1",
"jest": "^29.7.0",
"npm-run-all": "^4.1.5",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"engines": {
"node": ">=18"
},
"eslintConfig": {
"extends": [
"@mediamonks/eslint-config",
"@mediamonks/eslint-config-typescript"
],
"ignorePatterns": [
"dist/"
],
"parserOptions": {
"project": "./tsconfig.json",
"tsconfigRootDir": "./"
}
}
}
45 changes: 45 additions & 0 deletions packages/eslint-plugin-react/rules/throttleHookCallback.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { RuleTester } from 'eslint';
import * as throttleHookCallback from './throttleHookCallback';

const ruleTester = new RuleTester({
parserOptions: { ecmaVersion: 6 },
});

ruleTester.run(`@mediamonks/react/${throttleHookCallback.name}`, throttleHookCallback.default, {
valid: [
{
code: 'useResizeObserver({}, useRafCallback(() => {}));',
},
{
code: 'useResizeObserver({}, useDebounceCallback(() => {}, 200));',
},
{
code: 'useResizeObserver({}, useThrottleCallback(() => {}, 200));',
},
],

invalid: [
{
code: 'useResizeObserver({}, function() {});',
errors: [{ message: 'Callback in hook should be throttled.' }],
},
{
code: 'useResizeObserver({}, () => {});',
errors: [{ message: 'Callback in hook should be throttled.' }],
},
{
code: `
function myFunction() {}
useResizeObserver({}, myFunction);
`,
errors: [{ message: 'Callback in hook should be throttled.' }],
},
{
code: `
const myFunction = () => {};
useResizeObserver({}, myFunction);
`,
errors: [{ message: 'Callback in hook should be throttled.' }],
},
],
});
Loading
Loading