From af4987e19f0530cfd9f06b95680db7d02acacbd7 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 16 Jan 2025 14:16:21 -0500 Subject: [PATCH 1/6] [ci] Automatically label PRs from core team (#32100) Adds a new `MAINTAINERS` file which contains github usernames of core team members. This file serves as documentation for core team membership and is also used to automatically label PRs from core. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32100). * #32101 * __->__ #32100 --- .github/workflows/shared_core_label.yml | 41 +++++++++++++++++++++++++ MAINTAINERS | 23 ++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 .github/workflows/shared_core_label.yml create mode 100644 MAINTAINERS diff --git a/.github/workflows/shared_core_label.yml b/.github/workflows/shared_core_label.yml new file mode 100644 index 0000000000000..da2c45465eee9 --- /dev/null +++ b/.github/workflows/shared_core_label.yml @@ -0,0 +1,41 @@ +name: (Shared) Core Label + +on: + pull_request_target: + +env: + TZ: /usr/share/zoneinfo/America/Los_Angeles + # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 + +jobs: + core_label: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check if actor is maintainer + id: check_maintainer + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const actor = '${{ github.actor }}'; + const data = await fs.readFileSync('./MAINTAINERS', { encoding: 'utf8' }); + const maintainers = new Set(data.split('\n')); + if (maintainers.has(actor)) { + console.log(`🟢 ${actor} is a maintainer`); + return true; + } + console.log(`🔴 ${actor} is NOT a maintainer`); + return null; + - name: Label PR as React Core Team + if: ${{ steps.check_maintainer.outputs.result }} + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ github.event.number }}, + labels: ['React Core Team'] + }); diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000000000..3eda4c5b2d382 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1,23 @@ +acdlite +bvaughn +eps1lon +gaearon +gnoff +gsathya +hoxyq +jackpope +jbonta +josephsavona +kassens +lunaleaps +mattcarrollcode +mofeiZ +noahlemen +poteto +rickhanlonii +sebmarkbage +sethwebster +sophiebits +TheSavior +tyao1 +yuzhi From 60c797e744636a6d39afe03edd18fb3af9e04157 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 16 Jan 2025 14:21:55 -0500 Subject: [PATCH 2/6] [ci] Use shared maintainer check for discord notifications (#32101) Uses the shared maintainer check workflow across the various workflows that need it --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32101). * __->__ #32101 * #32100 --- .github/workflows/compiler_discord_notify.yml | 7 ++-- .github/workflows/runtime_discord_notify.yml | 7 ++-- .github/workflows/shared_check_maintainer.yml | 35 +++++++++++++++++++ .../workflows/shared_label_core_team_prs.yml | 29 +++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/shared_check_maintainer.yml create mode 100644 .github/workflows/shared_label_core_team_prs.yml diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index 3eeb009a78aab..febd55764b7a7 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -2,14 +2,17 @@ name: (Compiler) Discord Notify on: pull_request_target: - types: [labeled] paths: - compiler/** - .github/workflows/compiler_**.yml jobs: + check_maintainer: + uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main + notify: - if: ${{ github.event.label.name == 'React Core Team' }} + if: ${{ needs.check_maintainer.outputs.is_core_team }} + needs: check_maintainer runs-on: ubuntu-latest steps: - name: Discord Webhook Action diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index 93606cd549873..18304046d7767 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -2,14 +2,17 @@ name: (Runtime) Discord Notify on: pull_request_target: - types: [labeled] paths-ignore: - compiler/** - .github/workflows/compiler_**.yml jobs: + check_maintainer: + uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main + notify: - if: ${{ github.event.label.name == 'React Core Team' }} + if: ${{ needs.check_maintainer.outputs.is_core_team }} + needs: check_maintainer runs-on: ubuntu-latest steps: - name: Discord Webhook Action diff --git a/.github/workflows/shared_check_maintainer.yml b/.github/workflows/shared_check_maintainer.yml new file mode 100644 index 0000000000000..53f4c7a8af046 --- /dev/null +++ b/.github/workflows/shared_check_maintainer.yml @@ -0,0 +1,35 @@ +name: (Shared) Check maintainer + +on: + workflow_call: + outputs: + is_core_team: + value: ${{ jobs.check_maintainer.outputs.is_core_team }} + +env: + TZ: /usr/share/zoneinfo/America/Los_Angeles + # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 + +jobs: + check_maintainer: + runs-on: ubuntu-latest + outputs: + is_core_team: ${{ steps.check_if_actor_is_maintainer.outputs.result }} + steps: + - uses: actions/checkout@v4 + - name: Check if actor is maintainer + id: check_if_actor_is_maintainer + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const actor = '${{ github.actor }}'; + const data = await fs.readFileSync('./MAINTAINERS', { encoding: 'utf8' }); + const maintainers = new Set(data.split('\n')); + if (maintainers.has(actor)) { + console.log(`🟢 ${actor} is a maintainer`); + return true; + } + console.log(`🔴 ${actor} is NOT a maintainer`); + return null; diff --git a/.github/workflows/shared_label_core_team_prs.yml b/.github/workflows/shared_label_core_team_prs.yml new file mode 100644 index 0000000000000..b96aea88054fa --- /dev/null +++ b/.github/workflows/shared_label_core_team_prs.yml @@ -0,0 +1,29 @@ +name: (Shared) Label Core Team PRs + +on: + pull_request_target: + +env: + TZ: /usr/share/zoneinfo/America/Los_Angeles + # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout + SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 + +jobs: + check_maintainer: + uses: facebook/react/.github/workflows/shared_check_maintainer.yml@main + + label: + if: ${{ needs.check_maintainer.outputs.is_core_team }} + runs-on: ubuntu-latest + needs: check_maintainer + steps: + - name: Label PR as React Core Team + uses: actions/github-script@v7 + with: + script: | + github.rest.issues.addLabels({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ${{ github.event.number }}, + labels: ['React Core Team'] + }); From 5a274a37e05ba627e3e9c8a1000bee881aeaef1c Mon Sep 17 00:00:00 2001 From: Jordan Brown Date: Thu, 16 Jan 2025 14:53:11 -0500 Subject: [PATCH 3/6] Add Jordan B to maintainers I am React --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 3eda4c5b2d382..f4b470810636f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7,6 +7,7 @@ gsathya hoxyq jackpope jbonta +jbrown215 josephsavona kassens lunaleaps From 91add7bbdc1a06f9c6b71f3b41590266481b56cc Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 16 Jan 2025 15:05:40 -0500 Subject: [PATCH 4/6] Fix outdated maintainers list (#32102) I made a few mistakes while adding the initial list --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32102). * #32103 * __->__ #32102 --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index f4b470810636f..f204a5ca66def 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1,5 +1,4 @@ acdlite -bvaughn eps1lon gaearon gnoff @@ -13,7 +12,9 @@ kassens lunaleaps mattcarrollcode mofeiZ +mvitousek noahlemen +pieterv poteto rickhanlonii sebmarkbage From 1185f88d35e83870aa220a30ddcec22ed7e8e362 Mon Sep 17 00:00:00 2001 From: lauren Date: Thu, 16 Jan 2025 15:12:11 -0500 Subject: [PATCH 5/6] [ci] Only notify after labeling (#32103) --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32103). * __->__ #32103 * #32102 --- .github/workflows/compiler_discord_notify.yml | 1 + .github/workflows/runtime_discord_notify.yml | 1 + .github/workflows/shared_core_label.yml | 41 ------------------- 3 files changed, 2 insertions(+), 41 deletions(-) delete mode 100644 .github/workflows/shared_core_label.yml diff --git a/.github/workflows/compiler_discord_notify.yml b/.github/workflows/compiler_discord_notify.yml index febd55764b7a7..b9cc3e9b9fa83 100644 --- a/.github/workflows/compiler_discord_notify.yml +++ b/.github/workflows/compiler_discord_notify.yml @@ -2,6 +2,7 @@ name: (Compiler) Discord Notify on: pull_request_target: + types: [labeled] paths: - compiler/** - .github/workflows/compiler_**.yml diff --git a/.github/workflows/runtime_discord_notify.yml b/.github/workflows/runtime_discord_notify.yml index 18304046d7767..abf7970a1e634 100644 --- a/.github/workflows/runtime_discord_notify.yml +++ b/.github/workflows/runtime_discord_notify.yml @@ -2,6 +2,7 @@ name: (Runtime) Discord Notify on: pull_request_target: + types: [labeled] paths-ignore: - compiler/** - .github/workflows/compiler_**.yml diff --git a/.github/workflows/shared_core_label.yml b/.github/workflows/shared_core_label.yml deleted file mode 100644 index da2c45465eee9..0000000000000 --- a/.github/workflows/shared_core_label.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: (Shared) Core Label - -on: - pull_request_target: - -env: - TZ: /usr/share/zoneinfo/America/Los_Angeles - # https://github.com/actions/cache/blob/main/tips-and-workarounds.md#cache-segment-restore-timeout - SEGMENT_DOWNLOAD_TIMEOUT_MINS: 1 - -jobs: - core_label: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check if actor is maintainer - id: check_maintainer - uses: actions/github-script@v7 - with: - script: | - const fs = require('fs'); - const actor = '${{ github.actor }}'; - const data = await fs.readFileSync('./MAINTAINERS', { encoding: 'utf8' }); - const maintainers = new Set(data.split('\n')); - if (maintainers.has(actor)) { - console.log(`🟢 ${actor} is a maintainer`); - return true; - } - console.log(`🔴 ${actor} is NOT a maintainer`); - return null; - - name: Label PR as React Core Team - if: ${{ steps.check_maintainer.outputs.result }} - uses: actions/github-script@v7 - with: - script: | - github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: ${{ github.event.number }}, - labels: ['React Core Team'] - }); From 61e713c1d31976175316c8256f4be14ba8bbdb29 Mon Sep 17 00:00:00 2001 From: michael faith Date: Thu, 16 Jan 2025 17:39:11 -0600 Subject: [PATCH 6/6] feat(eslint-plugin-react-hooks): support flat config (#30774) --- packages/eslint-plugin-react-hooks/README.md | 38 ++++++++++++- .../eslint-plugin-react-hooks/src/index.js | 55 ++++++++++++++++--- 2 files changed, 82 insertions(+), 11 deletions(-) diff --git a/packages/eslint-plugin-react-hooks/README.md b/packages/eslint-plugin-react-hooks/README.md index d7a3e7d39cdee..36f63722f7268 100644 --- a/packages/eslint-plugin-react-hooks/README.md +++ b/packages/eslint-plugin-react-hooks/README.md @@ -18,21 +18,38 @@ npm install eslint-plugin-react-hooks --save-dev yarn add eslint-plugin-react-hooks --dev ``` -Then extend the recommended eslint config: +### Legacy Config (.eslintrc) + +If you are still using ESLint below 9.0.0, please continue to use `recommended-legacy`. To avoid breaking changes, we still support `recommended` as well, but note that this will be changed to alias the flat recommended config in v6. ```js { "extends": [ // ... - "plugin:react-hooks/recommended" + "plugin:react-hooks/recommended-legacy" ] } ``` +### Flat Config (eslint.config.js) + +For [ESLint 9.0.0 and above](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/) users, add the `recommended-latest` config. + +```js +import reactHooks from 'eslint-plugin-react-hooks'; + +export default [ + // ... + reactHooks.configs['recommended-latest'], +]; +``` + ### Custom Configuration If you want more fine-grained configuration, you can instead add a snippet like this to your ESLint configuration file: +#### Legacy Config (.eslintrc) + ```js { "plugins": [ @@ -47,6 +64,23 @@ If you want more fine-grained configuration, you can instead add a snippet like } ``` +#### Flat Config (eslint.config.js) + +```js +import reactHooks from 'eslint-plugin-react-hooks'; + +export default [ + { + files: ['**/*.{js,jsx}'], + plugins: { 'react-hooks': reactHooks }, + // ... + rules: { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', + } + }, +]; +``` ## Advanced Configuration diff --git a/packages/eslint-plugin-react-hooks/src/index.js b/packages/eslint-plugin-react-hooks/src/index.js index d8ff02e7b144f..c9da639a4fff6 100644 --- a/packages/eslint-plugin-react-hooks/src/index.js +++ b/packages/eslint-plugin-react-hooks/src/index.js @@ -10,17 +10,54 @@ import RulesOfHooks from './RulesOfHooks'; import ExhaustiveDeps from './ExhaustiveDeps'; -export const configs = { - recommended: { - plugins: ['react-hooks'], - rules: { - 'react-hooks/rules-of-hooks': 'error', - 'react-hooks/exhaustive-deps': 'warn', - }, - }, -}; +const {name, version} = require('../package.json'); +// All rules export const rules = { 'rules-of-hooks': RulesOfHooks, 'exhaustive-deps': ExhaustiveDeps, }; + +// Config rules +const configRules = { + 'react-hooks/rules-of-hooks': 'error', + 'react-hooks/exhaustive-deps': 'warn', +}; + +// Legacy config +const legacyRecommendedConfig = { + plugins: ['react-hooks'], + rules: configRules, +}; + +// Base plugin object +const reactHooksPlugin = { + meta: {name, version}, + rules, +}; + +// Flat config +const flatRecommendedConfig = { + name: 'react-hooks/recommended', + plugins: {'react-hooks': reactHooksPlugin}, + rules: configRules, +}; + +export const configs = { + /** Legacy recommended config, to be used with rc-based configurations */ + 'recommended-legacy': legacyRecommendedConfig, + + /** Latest recommended config, to be used with flat configurations */ + 'recommended-latest': flatRecommendedConfig, + + /** + * 'recommended' is currently aliased to the legacy / rc recommended config) to maintain backwards compatibility. + * This is deprecated and in v6, it will switch to alias the flat recommended config. + */ + recommended: legacyRecommendedConfig, +}; + +export default { + ...reactHooksPlugin, + configs, +};