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

Update all non-major dependencies #1567

Merged
merged 1 commit into from
Jan 2, 2025
Merged

Update all non-major dependencies #1567

merged 1 commit into from
Jan 2, 2025

Conversation

renovate[bot]
Copy link
Contributor

@renovate renovate bot commented Jan 1, 2025

This PR contains the following updates:

Package Change Age Adoption Passing Confidence
eslint-plugin-react 7.37.2 -> 7.37.3 age adoption passing confidence
lint-staged 15.2.11 -> 15.3.0 age adoption passing confidence

Release Notes

jsx-eslint/eslint-plugin-react (eslint-plugin-react)

v7.37.3

Compare Source

Fixed
  • [no-danger][no-danger]: avoid a crash on a nested component name ([#​3833][] @​ljharb)
  • [Fix] types: correct generated type declaration ([#​3840][] @​ocavue)
  • [no-unknown-property][no-unknown-property]: support precedence prop in react 19 ([#​3829][] @​acusti)
  • [prop-types][prop-types]: props missing in validation when using generic types from a namespace import ([#​3859][] @​rbondoc96)
Changed
  • [Tests] [jsx-no-script-url][jsx-no-script-url]: Improve tests ([#​3849][] @​radu2147)
  • [Docs] fix broken links: [default-props-match-prop-types][default-props-match-prop-types], [jsx-boolean-value][jsx-boolean-value], [jsx-curly-brace-presence][jsx-curly-brace-presence], [jsx-no-bind][jsx-no-bind], [no-array-index-key][no-array-index-key], [no-is-mounted][no-is-mounted], [no-render-return-value][no-render-return-value], [require-default-props][require-default-props] ([#​3841][] @​bastiendmt)

Configuration

📅 Schedule: Branch creation - "* 0-4 * * 3" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot added the dependencies Pull requests that update a dependency file label Jan 1, 2025
@renovate renovate bot force-pushed the renovate/all-minor-patch branch from 847216b to 0123ab7 Compare January 1, 2025 12:17
@renovate renovate bot changed the title Update dependency eslint-plugin-react to v7.37.3 Update all non-major dependencies Jan 1, 2025
@renovate renovate bot force-pushed the renovate/all-minor-patch branch from 0123ab7 to 7b72901 Compare January 2, 2025 17:57
Copy link

github-actions bot commented Jan 2, 2025

[puLL-Merge] - jsx-eslint/[email protected]

Diff
diff --git .eslintrc .eslintrc
index fd3ae2193c..894df7caa9 100644
--- .eslintrc
+++ .eslintrc
@@ -17,7 +17,8 @@
 		"coverage/",
 		".nyc_output/",
 		"test-published-types/",
-		"tests/fixtures/flat-config/"
+		"tests/fixtures/flat-config/",
+		"**/*/*.d.ts",
 	],
 	"rules": {
 		"comma-dangle": [2, "always-multiline"],
diff --git .github/workflows/node-18+.yml .github/workflows/node-18+.yml
index ee7d126939..5a48b15a88 100644
--- .github/workflows/node-18+.yml
+++ .github/workflows/node-18+.yml
@@ -104,7 +104,7 @@ jobs:
         with:
           node-version: ${{ matrix.node-version }}
           after_install: |
-            npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@${{ matrix.typescript-eslint }}" "babel-eslint@${{ matrix.babel-eslint }}"
+            npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@${{ matrix.typescript-eslint == 8 && 8.17 || matrix.typescript-eslint }}" "babel-eslint@${{ matrix.babel-eslint }}"
         env:
           NPM_CONFIG_LEGACY_PEER_DEPS: "${{ matrix.typescript-eslint >= 6 && 'false' || 'true' }}"
       - run: npx ls-engines
diff --git .github/workflows/node-minors.yml .github/workflows/node-minors.yml
index 69f92684ea..9e227f2b89 100644
--- .github/workflows/node-minors.yml
+++ .github/workflows/node-minors.yml
@@ -100,7 +100,7 @@ jobs:
         with:
           node-version: ${{ matrix.node-version }}
           after_install: |
-            npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@${{ matrix.node-version >= 18 && matrix.eslint >= 8 && '8' || (matrix.node-version >= 16 && matrix.eslint >= 7 && '6' || (matrix.node-version >= 14 && '5' || (matrix.node-version >= 12 && '4' || (matrix.node-version >= 10 && '4.0' || (matrix.node-version >= 8 && '3' || '2'))))) }}" "babel-eslint@${{ matrix.babel-eslint }}"
+            npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@${{ matrix.node-version >= 18 && matrix.eslint >= 8 && '8.17' || (matrix.node-version >= 16 && matrix.eslint >= 7 && '6' || (matrix.node-version >= 14 && '5' || (matrix.node-version >= 12 && '4' || (matrix.node-version >= 10 && '4.0' || (matrix.node-version >= 8 && '3' || '2'))))) }}" "babel-eslint@${{ matrix.babel-eslint }}"
           skip-ls-check: ${{ matrix.node-version < 10 && true || false }}
         env:
           NPM_CONFIG_LEGACY_PEER_DEPS: "${{ matrix.node-version >= 16 && matrix.eslint >= 7 && 'false' || 'true' }}"
diff --git .github/workflows/npm-publish.yml .github/workflows/npm-publish.yml
index 8ed3becf6b..004c88360e 100644
--- .github/workflows/npm-publish.yml
+++ .github/workflows/npm-publish.yml
@@ -63,7 +63,10 @@ jobs:
             const pending = checkSuites.filter(({ status }) => status !== 'completed')
 
             if (pending.length > 0) {
-              core.setFailed(`Some workflows for ${context.payload.inputs.tag} are still in-progress: ${JSON.stringify(pending)}`);
+              core.setFailed(`Some workflows for ${context.payload.inputs.tag} are still in-progress`);
+              pending.forEach(({ pull_requests, ...x }) => {
+                core.debug(JSON.stringify(x));
+              });
             }
 
             const result = await Promise.all(
@@ -94,7 +97,7 @@ jobs:
       contents: read
       id-token: write
     steps:
-      - uses: step-security/harden-runner@v1
+      - uses: step-security/harden-runner@v2
         with:
           egress-policy: block
           allowed-endpoints: >
diff --git .github/workflows/release.yml .github/workflows/release.yml
index 3a957f007c..d6f97d2e84 100644
--- .github/workflows/release.yml
+++ .github/workflows/release.yml
@@ -15,11 +15,14 @@ jobs:
       contents: write
 
     steps:
-      - uses: step-security/harden-runner@v1
+      - uses: step-security/harden-runner@v2
         with:
           allowed-endpoints:
             api.github.com:443
             github.com:443
+            raw.githubusercontent.com:443
+            nodejs.org:443
+            registry.npmjs.org:443
 
       - name: Get version from tag
         id: tag_name
diff --git .github/workflows/type-check.yml .github/workflows/type-check.yml
index c0e6b4d033..0aaca2cb00 100644
--- .github/workflows/type-check.yml
+++ .github/workflows/type-check.yml
@@ -44,18 +44,34 @@ jobs:
       - name: build types
         run: npm run build-types
 
+      # Pack the lib into a tarball so that when we install the lib later in the
+      # test-published-types directory, it's only install `dependencies` of the
+      # lib.
+      - name: pack the lib
+        run: npm pack --pack-destination /tmp/
+
+      - name: find the packed lib
+        run: echo "ESLINT_PLUGIN_REACT_PATH=$(ls /tmp/eslint-plugin-react*.tgz | tail -n 1)" >> $GITHUB_ENV
+
+      - name: show the path to the packed lib
+        run: echo "$ESLINT_PLUGIN_REACT_PATH"
+
       - name: npm install working directory
         run: npm install
         working-directory: test-published-types
 
-      - name: install typescript version ${{ matrix.ts_version }}
-        run: npm install --no-save typescript@${{ matrix.ts_version }}
+      - name: install eslint-plugin-react and typescript version ${{ matrix.ts_version }}
+        run: npm install --no-save "$ESLINT_PLUGIN_REACT_PATH" typescript@${{ matrix.ts_version }}
         working-directory: test-published-types
 
       - name: show installed typescript version
         run: npm list typescript --depth=0
         working-directory: test-published-types
 
+      - name: show installed eslint-plugin-react version
+        run: npm list eslint-plugin-react --depth=0
+        working-directory: test-published-types
+
       - name: check types with lib "${{ matrix.ts_lib }}"
         run: npx tsc --lib ${{ matrix.ts_lib }}
         working-directory: test-published-types
diff --git CHANGELOG.md CHANGELOG.md
index 26389bca1e..a7b13f285b 100644
--- CHANGELOG.md
+++ CHANGELOG.md
@@ -1,3 +1,4 @@
+
 # Change Log
 
 All notable changes to this project will be documented in this file.
@@ -6,6 +7,26 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
 
 ## Unreleased
 
+## [7.37.3] - 2024.12.23
+
+### Fixed
+* [`no-danger`]: avoid a crash on a nested component name ([#3833][] @ljharb)
+* [Fix] types: correct generated type declaration ([#3840][] @ocavue)
+* [`no-unknown-property`]: support `precedence` prop in react 19 ([#3829][] @acusti)
+* [`prop-types`]: props missing in validation when using generic types from a namespace import ([#3859][] @rbondoc96)
+
+### Changed
+* [Tests] [`jsx-no-script-url`]: Improve tests ([#3849][] @radu2147)
+* [Docs] fix broken links: [`default-props-match-prop-types`], [`jsx-boolean-value`], [`jsx-curly-brace-presence`], [`jsx-no-bind`], [`no-array-index-key`], [`no-is-mounted`], [`no-render-return-value`], [`require-default-props`] ([#3841][] @bastiendmt)
+
+[7.37.3]: https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.2...v7.37.3
+[#3859]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3859
+[#3849]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3849
+[#3841]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3841
+[#3840]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3840
+[#3833]: https://github.com/jsx-eslint/eslint-plugin-react/issues/3833
+[#3829]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3829
+
 ## [7.37.2] - 2024.10.22
 
 ### Fixed
diff --git index.js index.js
index 8426993e5b..365f17446c 100644
--- index.js
+++ index.js
@@ -11,7 +11,7 @@ function filterRules(rules, predicate) {
 
 /**
  * @param {object} rules - rules object mapping rule name to rule module
- * @returns {Record<string, 2 | 'error'>}
+ * @returns {Record<string, SEVERITY_ERROR | 'error'>}
  */
 function configureAsError(rules) {
   return fromEntries(Object.keys(rules).map((key) => [`react/${key}`, 2]));
@@ -31,6 +31,10 @@ const plugins = [
   'react',
 ];
 
+// TODO: with TS 4.5+, inline this
+const SEVERITY_ERROR = /** @type {2} */ (2);
+const SEVERITY_OFF = /** @type {0} */ (0);
+
 const configs = {
   recommended: {
     plugins,
@@ -40,28 +44,28 @@ const configs = {
       },
     },
     rules: {
-      'react/display-name': 2,
-      'react/jsx-key': 2,
-      'react/jsx-no-comment-textnodes': 2,
-      'react/jsx-no-duplicate-props': 2,
-      'react/jsx-no-target-blank': 2,
-      'react/jsx-no-undef': 2,
-      'react/jsx-uses-react': 2,
-      'react/jsx-uses-vars': 2,
-      'react/no-children-prop': 2,
-      'react/no-danger-with-children': 2,
-      'react/no-deprecated': 2,
-      'react/no-direct-mutation-state': 2,
-      'react/no-find-dom-node': 2,
-      'react/no-is-mounted': 2,
-      'react/no-render-return-value': 2,
-      'react/no-string-refs': 2,
-      'react/no-unescaped-entities': 2,
-      'react/no-unknown-property': 2,
-      'react/no-unsafe': 0,
-      'react/prop-types': 2,
-      'react/react-in-jsx-scope': 2,
-      'react/require-render-return': 2,
+      'react/display-name': SEVERITY_ERROR,
+      'react/jsx-key': SEVERITY_ERROR,
+      'react/jsx-no-comment-textnodes': SEVERITY_ERROR,
+      'react/jsx-no-duplicate-props': SEVERITY_ERROR,
+      'react/jsx-no-target-blank': SEVERITY_ERROR,
+      'react/jsx-no-undef': SEVERITY_ERROR,
+      'react/jsx-uses-react': SEVERITY_ERROR,
+      'react/jsx-uses-vars': SEVERITY_ERROR,
+      'react/no-children-prop': SEVERITY_ERROR,
+      'react/no-danger-with-children': SEVERITY_ERROR,
+      'react/no-deprecated': SEVERITY_ERROR,
+      'react/no-direct-mutation-state': SEVERITY_ERROR,
+      'react/no-find-dom-node': SEVERITY_ERROR,
+      'react/no-is-mounted': SEVERITY_ERROR,
+      'react/no-render-return-value': SEVERITY_ERROR,
+      'react/no-string-refs': SEVERITY_ERROR,
+      'react/no-unescaped-entities': SEVERITY_ERROR,
+      'react/no-unknown-property': SEVERITY_ERROR,
+      'react/no-unsafe': SEVERITY_OFF,
+      'react/prop-types': SEVERITY_ERROR,
+      'react/react-in-jsx-scope': SEVERITY_ERROR,
+      'react/require-render-return': SEVERITY_ERROR,
     },
   },
   all: {
@@ -82,8 +86,8 @@ const configs = {
       jsxPragma: null, // for @typescript/eslint-parser
     },
     rules: {
-      'react/react-in-jsx-scope': 0,
-      'react/jsx-uses-react': 0,
+      'react/react-in-jsx-scope': SEVERITY_OFF,
+      'react/jsx-uses-react': SEVERITY_OFF,
     },
   },
 };
diff --git lib/rules/forbid-foreign-prop-types.js lib/rules/forbid-foreign-prop-types.js
index 7724049af4..20d191d264 100644
--- lib/rules/forbid-foreign-prop-types.js
+++ lib/rules/forbid-foreign-prop-types.js
@@ -109,7 +109,7 @@ module.exports = {
             && !ast.isAssignmentLHS(node)
             && !isAllowedAssignment(node)
           )) || (
-            // @ts-expect-error The JSXText type is not present in the estree type definitions
+            // @ts-expect-error: The JSXText type is not present in the estree type definitions
             (node.property.type === 'Literal' || node.property.type === 'JSXText')
             && 'value' in node.property
             && node.property.value === 'propTypes'
diff --git lib/rules/forbid-prop-types.js lib/rules/forbid-prop-types.js
index df40706d00..44de1fa023 100644
--- lib/rules/forbid-prop-types.js
+++ lib/rules/forbid-prop-types.js
@@ -195,6 +195,7 @@ module.exports = {
             const propTypesSpecifier = node.specifiers.find((specifier) => (
               'imported' in specifier
               && specifier.imported
+              && 'name' in specifier.imported
               && specifier.imported.name === 'PropTypes'
             ));
             if (propTypesSpecifier) {
diff --git lib/rules/forward-ref-uses-ref.js lib/rules/forward-ref-uses-ref.js
index aeedeb82df..3a0b7de4c9 100644
--- lib/rules/forward-ref-uses-ref.js
+++ lib/rules/forward-ref-uses-ref.js
@@ -41,6 +41,7 @@ const messages = {
   removeForwardRef: 'Remove forwardRef wrapper',
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
     docs: {
diff --git lib/rules/index.js lib/rules/index.js
index 1e010b677b..0e73ab1a3f 100644
--- lib/rules/index.js
+++ lib/rules/index.js
@@ -3,7 +3,7 @@
 /* eslint global-require: 0 */
 
 /** @satisfies {Record<string, import('eslint').Rule.RuleModule>} */
-module.exports = {
+const rules = {
   'boolean-prop-naming': require('./boolean-prop-naming'),
   'button-has-type': require('./button-has-type'),
   'checked-requires-onchange-or-readonly': require('./checked-requires-onchange-or-readonly'),
@@ -108,3 +108,5 @@ module.exports = {
   'style-prop-object': require('./style-prop-object'),
   'void-dom-elements-no-children': require('./void-dom-elements-no-children'),
 };
+
+module.exports = rules;
diff --git lib/rules/jsx-fragments.js lib/rules/jsx-fragments.js
index 6835331025..b4a545846e 100644
--- lib/rules/jsx-fragments.js
+++ lib/rules/jsx-fragments.js
@@ -170,7 +170,12 @@ module.exports = {
       ImportDeclaration(node) {
         if (node.source && node.source.value === 'react') {
           node.specifiers.forEach((spec) => {
-            if ('imported' in spec && spec.imported && spec.imported.name === fragmentPragma) {
+            if (
+              'imported' in spec
+              && spec.imported
+              && 'name' in spec.imported
+              && spec.imported.name === fragmentPragma
+            ) {
               if (spec.local) {
                 fragmentNames.add(spec.local.name);
               }
diff --git lib/rules/jsx-handler-names.js lib/rules/jsx-handler-names.js
index 652e125d6e..f89f24413e 100644
--- lib/rules/jsx-handler-names.js
+++ lib/rules/jsx-handler-names.js
@@ -19,6 +19,14 @@ const messages = {
   badPropKey: 'Prop key for {{propValue}} must begin with \'{{handlerPropPrefix}}\'',
 };
 
+function isPrefixDisabled(prefix) {
+  return prefix === false;
+}
+
+function isInlineHandler(node) {
+  return node.value.expression.type === 'ArrowFunctionExpression';
+}
+
 /** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
@@ -109,14 +117,6 @@ module.exports = {
   },
 
   create(context) {
-    function isPrefixDisabled(prefix) {
-      return prefix === false;
-    }
-
-    function isInlineHandler(node) {
-      return node.value.expression.type === 'ArrowFunctionExpression';
-    }
-
     const configuration = context.options[0] || {};
 
     const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix)
@@ -143,10 +143,10 @@ module.exports = {
       JSXAttribute(node) {
         const componentName = node.parent.name.name;
 
-        const isComponentNameIgnored = ignoreComponentNames.some((ignoredComponentNamePattern) => {
-          const isIgnored = minimatch(componentName, ignoredComponentNamePattern);
-          return isIgnored;
-        });
+        const isComponentNameIgnored = ignoreComponentNames.some((ignoredComponentNamePattern) => minimatch(
+          componentName,
+          ignoredComponentNamePattern
+        ));
 
         if (
           !node.value
diff --git lib/rules/jsx-no-literals.js lib/rules/jsx-no-literals.js
index 230d33a18d..45c9a54c48 100644
--- lib/rules/jsx-no-literals.js
+++ lib/rules/jsx-no-literals.js
@@ -17,6 +17,14 @@ const docsUrl = require('../util/docsUrl');
 const report = require('../util/report');
 const getText = require('../util/eslint').getText;
 
+/** @typedef {import('eslint').Rule.RuleModule} RuleModule */
+
+/** @typedef {import('../../types/rules/jsx-no-literals').Config} Config */
+/** @typedef {import('../../types/rules/jsx-no-literals').RawConfig} RawConfig */
+/** @typedef {import('../../types/rules/jsx-no-literals').ResolvedConfig} ResolvedConfig */
+/** @typedef {import('../../types/rules/jsx-no-literals').OverrideConfig} OverrideConfig */
+/** @typedef {import('../../types/rules/jsx-no-literals').ElementConfig} ElementConfig */
+
 // ------------------------------------------------------------------------------
 // Rule Definition
 // ------------------------------------------------------------------------------
@@ -45,7 +53,7 @@ const messages = {
   literalNotInJSXExpressionInElement: 'Missing JSX expression container around literal string: "{{text}}" in {{element}}',
 };
 
-/** @type {Exclude<import('eslint').Rule.RuleModule['meta']['schema'], unknown[]>['properties']} */
+/** @type {Exclude<RuleModule['meta']['schema'], unknown[] | false>['properties']} */
 const commonPropertiesSchema = {
   noStrings: {
     type: 'boolean',
@@ -65,52 +73,7 @@ const commonPropertiesSchema = {
   },
 };
 
-/**
- * @typedef RawElementConfigProperties
- * @property {boolean} [noStrings]
- * @property {string[]} [allowedStrings]
- * @property {boolean} [ignoreProps]
- * @property {boolean} [noAttributeStrings]
- *
- * @typedef RawOverrideConfigProperties
- * @property {boolean} [allowElement]
- * @property {boolean} [applyToNestedElements=true]
- *
- * @typedef {RawElementConfigProperties} RawElementConfig
- * @typedef {RawElementConfigProperties & RawElementConfigProperties} RawOverrideConfig
- *
- * @typedef RawElementOverrides
- * @property {Record<string, RawOverrideConfig>} [elementOverrides]
- *
- * @typedef {RawElementConfig & RawElementOverrides} RawConfig
- *
- * ----------------------------------------------------------------------
- *
- * @typedef ElementConfigType
- * @property {'element'} type
- *
- * @typedef ElementConfigProperties
- * @property {boolean} noStrings
- * @property {Set<string>} allowedStrings
- * @property {boolean} ignoreProps
- * @property {boolean} noAttributeStrings
- *
- * @typedef OverrideConfigProperties
- * @property {'override'} type
- * @property {string} name
- * @property {boolean} allowElement
- * @property {boolean} applyToNestedElements
- *
- * @typedef {ElementConfigType & ElementConfigProperties} ElementConfig
- * @typedef {OverrideConfigProperties & ElementConfigProperties} OverrideConfig
- *
- * @typedef ElementOverrides
- * @property {Record<string, OverrideConfig>} elementOverrides
- *
- * @typedef {ElementConfig & ElementOverrides} Config
- * @typedef {Config | OverrideConfig} ResolvedConfig
- */
-
+// eslint-disable-next-line valid-jsdoc
 /**
  * Normalizes the element portion of the config
  * @param {RawConfig} config
@@ -128,6 +91,7 @@ function normalizeElementConfig(config) {
   };
 }
 
+// eslint-disable-next-line valid-jsdoc
 /**
  * Normalizes the config and applies default values to all config options
  * @param {RawConfig} config
@@ -182,8 +146,9 @@ const elementOverrides = {
   },
 };
 
+/** @type {RuleModule} */
 module.exports = {
-  meta: /** @type {import('eslint').Rule.RuleModule["meta"]} */ ({
+  meta: /** @type {RuleModule['meta']} */ ({
     docs: {
       description: 'Disallow usage of string literals in JSX',
       category: 'Stylistic Issues',
@@ -339,6 +304,7 @@ module.exports = {
       return some(iterFrom([ancestors.parent, ancestors.grandParent]), (parent) => jsxElementTypes.has(parent.type));
     }
 
+    // eslint-disable-next-line valid-jsdoc
     /**
      * Determines whether a given node's value and its immediate parent are
      * viable text nodes that can/should be reported on
@@ -370,6 +336,7 @@ module.exports = {
       return isStandardJSXNode && parent.type !== 'JSXExpressionContainer';
     }
 
+    // eslint-disable-next-line valid-jsdoc
     /**
      * Gets an override config for a given node. For any given node, we also
      * need to traverse the ancestor tree to determine if an ancestor's config
@@ -408,6 +375,7 @@ module.exports = {
       }
     }
 
+    // eslint-disable-next-line valid-jsdoc
     /**
      * @param {ResolvedConfig} resolvedConfig
      * @returns {boolean}
@@ -416,6 +384,7 @@ module.exports = {
       return resolvedConfig.type === 'override' && 'allowElement' in resolvedConfig && !!resolvedConfig.allowElement;
     }
 
+    // eslint-disable-next-line valid-jsdoc
     /**
      * @param {boolean} ancestorIsJSXElement
      * @param {ResolvedConfig} resolvedConfig
@@ -433,6 +402,7 @@ module.exports = {
       return resolvedConfig.type === 'override' ? 'literalNotInJSXExpressionInElement' : 'literalNotInJSXExpression';
     }
 
+    // eslint-disable-next-line valid-jsdoc
     /**
      * @param {ASTNode} node
      * @param {string} messageId
diff --git lib/rules/jsx-props-no-spread-multi.js lib/rules/jsx-props-no-spread-multi.js
index 2eeed0be49..3103be86da 100644
--- lib/rules/jsx-props-no-spread-multi.js
+++ lib/rules/jsx-props-no-spread-multi.js
@@ -16,6 +16,7 @@ const messages = {
   noMultiSpreading: 'Spreading the same expression multiple times is forbidden',
 };
 
+/** @type {import('eslint').Rule.RuleModule} */
 module.exports = {
   meta: {
     docs: {
diff --git lib/rules/jsx-space-before-closing.js lib/rules/jsx-space-before-closing.js
index ffcc357d8b..2cef238841 100644
--- lib/rules/jsx-space-before-closing.js
+++ lib/rules/jsx-space-before-closing.js
@@ -59,7 +59,7 @@ module.exports = {
         const sourceCode = getSourceCode(context);
 
         const leftToken = getTokenBeforeClosingBracket(node);
-        const closingSlash = /** @type {import("eslint").AST.Token} */ (sourceCode.getTokenAfter(leftToken));
+        const closingSlash = /** @type {import('eslint').AST.Token} */ (sourceCode.getTokenAfter(leftToken));
 
         if (leftToken.loc.end.line !== closingSlash.loc.start.line) {
           return;
diff --git lib/rules/no-access-state-in-setstate.js lib/rules/no-access-state-in-setstate.js
index 7126517112..1b23dd88ee 100644
--- lib/rules/no-access-state-in-setstate.js
+++ lib/rules/no-access-state-in-setstate.js
@@ -116,7 +116,7 @@ module.exports = {
           && node.object.type === 'ThisExpression'
           && isClassComponent(node)
         ) {
-          /** @type {import("eslint").Rule.Node} */
+          /** @type {import('eslint').Rule.Node} */
           let current = node;
           while (current.type !== 'Program') {
             // Reporting if this.state is directly within this.setState
@@ -163,7 +163,7 @@ module.exports = {
 
       Identifier(node) {
         // Checks if the identifier is a variable within an object
-        /** @type {import("eslint").Rule.Node} */
+        /** @type {import('eslint').Rule.Node} */
         let current = node;
         while (current.parent.type === 'BinaryExpression') {
           current = current.parent;
diff --git lib/rules/no-danger.js lib/rules/no-danger.js
index fb70263208..54dbed49cf 100644
--- lib/rules/no-danger.js
+++ lib/rules/no-danger.js
@@ -77,7 +77,8 @@ module.exports = {
 
     return {
       JSXAttribute(node) {
-        const functionName = node.parent.name.name;
+        const nodeName = node.parent.name;
+        const functionName = nodeName.name || `${nodeName.object.name}.${nodeName.property.name}`;
 
         const enableCheckingCustomComponent = customComponentNames.some((name) => minimatch(functionName, name));
 
diff --git lib/rules/no-deprecated.js lib/rules/no-deprecated.js
index 9f5ddf7082..0c5931345f 100644
--- lib/rules/no-deprecated.js
+++ lib/rules/no-deprecated.js
@@ -229,7 +229,7 @@ module.exports = {
         }
         node.specifiers.filter(((s) => 'imported' in s && s.imported)).forEach((specifier) => {
           // TODO, semver-major: remove `in` check as part of jsdoc->tsdoc migration
-          checkDeprecation(node, 'imported' in specifier && `${MODULES[node.source.value][0]}.${specifier.imported.name}`, specifier);
+          checkDeprecation(node, 'imported' in specifier && 'name' in specifier.imported && `${MODULES[node.source.value][0]}.${specifier.imported.name}`, specifier);
         });
       },
 
diff --git lib/rules/no-unknown-property.js lib/rules/no-unknown-property.js
index dc5018007a..9a21c96fad 100644
--- lib/rules/no-unknown-property.js
+++ lib/rules/no-unknown-property.js
@@ -363,16 +363,24 @@ const REACT_ON_PROPS = [
 ];
 
 function getDOMPropertyNames(context) {
-  const ALL_DOM_PROPERTY_NAMES = DOM_PROPERTY_NAMES_TWO_WORDS.concat(DOM_PROPERTY_NAMES_ONE_WORD);
-  // this was removed in React v16.1+, see https://github.com/facebook/react/pull/10823
-  if (!testReactVersion(context, '>= 16.1.0')) {
-    return ALL_DOM_PROPERTY_NAMES.concat('allowTransparency');
-  }
-  // these were added in React v16.4.0, see https://reactjs.org/blog/2018/05/23/react-v-16-4.html and https://github.com/facebook/react/pull/12507
-  if (testReactVersion(context, '>= 16.4.0')) {
-    return ALL_DOM_PROPERTY_NAMES.concat(REACT_ON_PROPS);
-  }
-  return ALL_DOM_PROPERTY_NAMES;
+  return [].concat(
+    DOM_PROPERTY_NAMES_TWO_WORDS,
+    DOM_PROPERTY_NAMES_ONE_WORD,
+
+    testReactVersion(context, '>= 16.1.0') ? [].concat(
+      testReactVersion(context, '>= 16.4.0') ? [].concat(
+        // these were added in React v16.4.0, see https://reactjs.org/blog/2018/05/23/react-v-16-4.html and https://github.com/facebook/react/pull/12507
+        REACT_ON_PROPS,
+        testReactVersion(context, '>= 19') ? [
+          // precedence was added in React v19, see https://react.dev/blog/2024/04/25/react-19#support-for-stylesheets
+          'precedence',
+        ] : []
+      ) : []
+    ) : [
+      // this was removed in React v16.1+, see https://github.com/facebook/react/pull/10823
+      'allowTransparency',
+    ]
+  );
 }
 
 // ------------------------------------------------------------------------------
@@ -501,6 +509,7 @@ function getStandardName(name, context) {
     return SVGDOM_ATTRIBUTE_NAMES[/** @type {keyof SVGDOM_ATTRIBUTE_NAMES} */ (name)];
   }
   const names = getDOMPropertyNames(context);
+
   // Let's find a possible attribute match with a case-insensitive search.
   return names.find((element) => element.toLowerCase() === name.toLowerCase());
 }
diff --git lib/rules/no-unused-state.js lib/rules/no-unused-state.js
index a71d63ebeb..c1986755c4 100644
--- lib/rules/no-unused-state.js
+++ lib/rules/no-unused-state.js
@@ -468,7 +468,7 @@ module.exports = {
           && unwrappedRight.type === 'ObjectExpression'
         ) {
           // Find the nearest function expression containing this assignment.
-          /** @type {import("eslint").Rule.Node} */
+          /** @type {import('eslint').Rule.Node} */
           let fn = node;
           while (fn.type !== 'FunctionExpression' && fn.parent) {
             fn = fn.parent;
diff --git lib/util/propTypes.js lib/util/propTypes.js
index f26a6510c9..3a8cc67f93 100644
--- lib/util/propTypes.js
+++ lib/util/propTypes.js
@@ -107,8 +107,10 @@ module.exports = function propTypesInstructions(context, components, utils) {
   const defaults = { customValidators: [] };
   const configuration = Object.assign({}, defaults, context.options[0] || {});
   const customValidators = configuration.customValidators;
-  const allowedGenericTypes = new Set(['forwardRef', 'ForwardRefRenderFunction', 'VFC', 'VoidFunctionComponent', 'PropsWithChildren', 'SFC', 'StatelessComponent', 'FunctionComponent', 'FC']);
+  const allowedGenericTypes = new Set(['ComponentProps', 'ComponentPropsWithoutRef', 'forwardRef', 'ForwardRefRenderFunction', 'VFC', 'VoidFunctionComponent', 'PropsWithChildren', 'SFC', 'StatelessComponent', 'FunctionComponent', 'FC']);
   const genericTypeParamIndexWherePropsArePresent = {
+    ComponentProps: 0,
+    ComponentPropsWithoutRef: 0,
     ForwardRefRenderFunction: 1,
     forwardRef: 1,
     VoidFunctionComponent: 0,
diff --git lib/util/version.js lib/util/version.js
index 9db44dbee2..c9dc683b7d 100644
--- lib/util/version.js
+++ lib/util/version.js
@@ -62,8 +62,7 @@ function readDefaultReactVersionFromContext(context) {
   if (context.settings && context.settings.react && context.settings.react.defaultVersion) {
     let settingsDefaultVersion = context.settings.react.defaultVersion;
     if (typeof settingsDefaultVersion !== 'string') {
-      error('Warning: default React version specified in eslint-pluigin-react-settings must be a string; '
-        + `got "${typeof settingsDefaultVersion}"`);
+      error(`Warning: default React version specified in eslint-pluigin-react-settings must be a string; got "${typeof settingsDefaultVersion}"`);
     }
     settingsDefaultVersion = String(settingsDefaultVersion);
     const result = convertConfVerToSemver(settingsDefaultVersion);
@@ -117,13 +116,11 @@ function getReactVersionFromContext(context) {
       settingsVersion = detectReactVersion(context);
     }
     if (typeof settingsVersion !== 'string') {
-      error('Warning: React version specified in eslint-plugin-react-settings must be a string; '
-        + `got “${typeof settingsVersion}”`);
+      error(`Warning: React version specified in eslint-plugin-react-settings must be a string; got “${typeof settingsVersion}”`);
     }
     confVer = String(settingsVersion);
   } else if (!warnedForMissingVersion) {
-    error('Warning: React version not specified in eslint-plugin-react settings. '
-      + 'See https://github.com/jsx-eslint/eslint-plugin-react#configuration .');
+    error('Warning: React version not specified in eslint-plugin-react settings. See https://github.com/jsx-eslint/eslint-plugin-react#configuration .');
     warnedForMissingVersion = true;
   }
 
diff --git package.json package.json
index 510549d6a8..9b34448353 100644
--- package.json
+++ package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint-plugin-react",
-  "version": "7.37.2",
+  "version": "7.37.3",
   "author": "Yannick Croissant <[email protected]>",
   "description": "React specific linting rules for ESLint",
   "main": "index.js",
@@ -26,39 +26,46 @@
     "type": "git",
     "url": "https://github.com/jsx-eslint/eslint-plugin-react"
   },
+  "directories": {
+    "test": [
+      "test",
+      "tests",
+      "test-published-types"
+    ]
+  },
   "homepage": "https://github.com/jsx-eslint/eslint-plugin-react",
   "bugs": "https://github.com/jsx-eslint/eslint-plugin-react/issues",
   "dependencies": {
     "array-includes": "^3.1.8",
     "array.prototype.findlast": "^1.2.5",
-    "array.prototype.flatmap": "^1.3.2",
+    "array.prototype.flatmap": "^1.3.3",
     "array.prototype.tosorted": "^1.1.4",
     "doctrine": "^2.1.0",
-    "es-iterator-helpers": "^1.1.0",
+    "es-iterator-helpers": "^1.2.1",
     "estraverse": "^5.3.0",
     "hasown": "^2.0.2",
     "jsx-ast-utils": "^2.4.1 || ^3.0.0",
     "minimatch": "^3.1.2",
     "object.entries": "^1.1.8",
     "object.fromentries": "^2.0.8",
-    "object.values": "^1.2.0",
+    "object.values": "^1.2.1",
     "prop-types": "^15.8.1",
     "resolve": "^2.0.0-next.5",
     "semver": "^6.3.1",
-    "string.prototype.matchall": "^4.0.11",
+    "string.prototype.matchall": "^4.0.12",
     "string.prototype.repeat": "^1.0.0"
   },
   "devDependencies": {
-    "@babel/core": "^7.25.9",
+    "@babel/core": "^7.26.0",
     "@babel/eslint-parser": "^7.25.9",
     "@babel/plugin-syntax-decorators": "^7.25.9",
     "@babel/plugin-syntax-do-expressions": "^7.25.9",
     "@babel/plugin-syntax-function-bind": "^7.25.9",
-    "@babel/preset-react": "^7.25.9",
+    "@babel/preset-react": "^7.26.3",
     "@types/eslint": "=7.2.10",
     "@types/estree": "0.0.52",
     "@types/node": "^4.9.5",
-    "@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4 || ^5 || ^6.20 || ^7.14.1 || ^8.4",
+    "@typescript-eslint/parser": "^2.34.0 || ^3.10.1 || ^4 || ^5 || ^6.20 || ^7.14.1 || 8.4 - 8.17",
     "babel-eslint": "^8 || ^9 || ^10.1.0",
     "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7",
     "eslint-config-airbnb-base": "^15.0.0",
diff --git test-published-types/index.js test-published-types/index.js
index 31e6005985..010d658041 100644
--- test-published-types/index.js
+++ test-published-types/index.js
@@ -3,10 +3,12 @@
 const react = require('eslint-plugin-react');
 
 /** @type {import('eslint').Linter.Config[]} */
-module.exports = [
+const config = [
   {
     plugins: {
       react,
     },
   },
 ];
+
+module.exports = config;
diff --git test-published-types/package.json test-published-types/package.json
index ab8a7160c6..80953c53ab 100644
--- test-published-types/package.json
+++ test-published-types/package.json
@@ -3,7 +3,6 @@
   "private": true,
   "version": "0.0.0",
   "dependencies": {
-    "eslint": "^9.11.1",
-    "eslint-plugin-react": "file:.."
+    "eslint": "^9.11.1"
   }
 }
diff --git test-published-types/tsconfig.json test-published-types/tsconfig.json
index 7fc1500df3..2ce0bd816e 100644
--- test-published-types/tsconfig.json
+++ test-published-types/tsconfig.json
@@ -7,6 +7,7 @@
 
   "compilerOptions": {
     "lib": ["esnext"],
-    "types": ["node"]
+    "types": ["node"],
+    "skipLibCheck": true
   }
 }
diff --git tests/index.js tests/index.js
index 9f7a052943..3539f48136 100644
--- tests/index.js
+++ tests/index.js
@@ -10,6 +10,7 @@ const plugin = require('..');
 const index = require('../lib/rules');
 
 const ruleFiles = fs.readdirSync(path.resolve(__dirname, '../lib/rules/'))
+  .filter((f) => f.endsWith('.js'))
   .map((f) => path.basename(f, '.js'))
   .filter((f) => f !== 'index');
 
diff --git tests/lib/rules/jsx-no-script-url.js tests/lib/rules/jsx-no-script-url.js
index 2a2ff394bd..b97c9ce567 100644
--- tests/lib/rules/jsx-no-script-url.js
+++ tests/lib/rules/jsx-no-script-url.js
@@ -38,6 +38,10 @@ ruleTester.run('jsx-no-script-url', rule, {
     { code: '<a href={"javascript:"}></a>' },
     { code: '<Foo href="javascript:"></Foo>' },
     { code: '<a href />' },
+    {
+      code: '<Foo other="javascript:"></Foo>',
+      options: [[{ name: 'Foo', props: ['to', 'href'] }]],
+    },
     {
       code: '<Foo href="javascript:"></Foo>',
       settings: {
@@ -51,6 +55,13 @@ ruleTester.run('jsx-no-script-url', rule, {
         linkComponents: [{ name: 'Foo', linkAttribute: ['to', 'href'] }],
       },
     },
+    {
+      code: '<Foo other="javascript:"></Foo>',
+      options: [[], { includeFromSettings: true }],
+      settings: {
+        linkComponents: [{ name: 'Foo', linkAttribute: ['to', 'href'] }],
+      },
+    },
   ]),
   invalid: parsers.all([
     // defaults
diff --git tests/lib/rules/jsx-uses-react.js tests/lib/rules/jsx-uses-react.js
index 3a8e39013c..381b44bc89 100644
--- tests/lib/rules/jsx-uses-react.js
+++ tests/lib/rules/jsx-uses-react.js
@@ -60,28 +60,67 @@ ruleTester.run('no-unused-vars', rule, {
   invalid: parsers.all([
     {
       code: '/*eslint react/jsx-uses-react:1*/ var React;',
-      errors: [{ message: '\'React\' is defined but never used.' }],
+      errors: [{
+        message: '\'React\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: '/*eslint react/jsx-uses-react:1*/ ',
+        }],
+      }],
     },
     {
       code: '/*eslint react/jsx-uses-react:1*/ /** @jsx Foo */ var React; <div />;',
-      errors: [{ message: '\'React\' is defined but never used.' }],
+      errors: [{
+        message: '\'React\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: '/*eslint react/jsx-uses-react:1*/ /** @jsx Foo */  <div />;',
+        }],
+      }],
     },
     {
       code: '/*eslint react/jsx-uses-react:1*/ var React; <div />;',
-      errors: [{ message: '\'React\' is defined but never used.' }],
+      errors: [{
+        message: '\'React\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: '/*eslint react/jsx-uses-react:1*/  <div />;',
+        }],
+      }],
       settings,
     },
     {
       code: '/*eslint react/jsx-uses-react:1*/ var Frag; <></>;',
-      errors: [{ message: '\'Frag\' is defined but never used.' }],
+      errors: [{
+        message: '\'Frag\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: '/*eslint react/jsx-uses-react:1*/  <></>;',
+        }],
+      }],
       features: ['fragment'],
       settings: { react: { fragment: 'Fragment' } },
     },
     {
       code: '/*eslint react/jsx-uses-react:1*/ var React; <></>;',
       features: ['fragment'],
-      errors: [{ message: '\'React\' is defined but never used.' }],
+      errors: [{
+        message: '\'React\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: '/*eslint react/jsx-uses-react:1*/  <></>;',
+        }],
+      }],
       settings,
     },
-  ].map(parsers.disableNewTS)),
+  ].map(parsers.disableNewTS).map((test) => {
+    if (!rule.meta.hasSuggestions) {
+      test.errors = test.errors.map((error) => {
+        // https://github.com/eslint/eslint/pull/18352 added suggestions to no-unused-vars in eslint v9.17.0
+        delete error.suggestions;
+        return error;
+      });
+    }
+    return test;
+  })),
 });
diff --git tests/lib/rules/jsx-uses-vars.js tests/lib/rules/jsx-uses-vars.js
index 00f801fd65..8b9db2a956 100644
--- tests/lib/rules/jsx-uses-vars.js
+++ tests/lib/rules/jsx-uses-vars.js
@@ -10,6 +10,7 @@
 // -----------------------------------------------------------------------------
 
 const ruleNoUnusedVars = require('../../helpers/getESLintCoreRule')('no-unused-vars');
+
 const rulePreferConst = require('../../helpers/getESLintCoreRule')('prefer-const');
 
 const RuleTester = require('../../helpers/ruleTester');
@@ -136,7 +137,13 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
   invalid: parsers.all([
     {
       code: '/* eslint react/jsx-uses-vars: 1 */ var App;',
-      errors: [{ message: '\'App\' is defined but never used.' }],
+      errors: [{
+        message: '\'App\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: '/* eslint react/jsx-uses-vars: 1 */ ',
+        }],
+      }],
     },
     {
       code: `
@@ -145,7 +152,18 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         var unused;
         React.render(<App unused=""/>);
       `,
-      errors: [{ message: '\'unused\' is defined but never used.' }],
+      errors: [{
+        message: '\'unused\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        var App;
+        ${''}
+        React.render(<App unused=""/>);
+      `,
+        }],
+      }],
     },
     {
       code: `
@@ -155,8 +173,30 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         React.render(<App:Hello/>);
       `,
       errors: [
-        { message: '\'App\' is defined but never used.' },
-        { message: '\'Hello\' is defined but never used.' },
+        {
+          message: '\'App\' is defined but never used.',
+          suggestions: [{
+            messageId: 'removeVar',
+            output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        ${''}
+        var Hello;
+        React.render(<App:Hello/>);
+      `,
+          }],
+        },
+        {
+          message: '\'Hello\' is defined but never used.',
+          suggestions: [{
+            messageId: 'removeVar',
+            output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        var App;
+        ${''}
+        React.render(<App:Hello/>);
+      `,
+          }],
+        },
       ],
       features: ['jsx namespace'],
     },
@@ -167,14 +207,34 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         var Input;
         React.render(<Button.Input unused=""/>);
       `,
-      errors: [{ message: '\'Input\' is defined but never used.' }],
+      errors: [{
+        message: '\'Input\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        var Button;
+        ${''}
+        React.render(<Button.Input unused=""/>);
+      `,
+        }],
+      }],
     },
     {
       code: `
         /* eslint react/jsx-uses-vars: 1 */
         class unused {}
       `,
-      errors: [{ message: '\'unused\' is defined but never used.' }],
+      errors: [{
+        message: '\'unused\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        ${''}
+      `,
+        }],
+      }],
     },
     {
       code: `
@@ -190,6 +250,13 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         {
           message: '\'HelloMessage\' is defined but never used.',
           line: 3,
+          suggestions: [{
+            messageId: 'removeVar',
+            output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        ${''}
+      `,
+          }],
         },
       ],
     },
@@ -207,6 +274,18 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         {
           message: '\'Hello\' is defined but never used.',
           line: 3,
+          suggestions: [{
+            messageId: 'removeVar',
+            output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        ${''}
+        function Greetings() {
+          const Hello = require('Hello').default;
+          return <Hello />;
+        }
+        Greetings();
+      `,
+          }],
         },
       ],
     },
@@ -216,7 +295,17 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         var lowercase;
         React.render(<lowercase />);
       `,
-      errors: [{ message: '\'lowercase\' is defined but never used.' }],
+      errors: [{
+        message: '\'lowercase\' is defined but never used.',
+        suggestions: [{
+          messageId: 'removeVar',
+          output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        ${''}
+        React.render(<lowercase />);
+      `,
+        }],
+      }],
     },
     {
       code: `
@@ -230,10 +319,29 @@ ruleTester.run('no-unused-vars', ruleNoUnusedVars, {
         {
           message: '\'div\' is defined but never used.',
           line: 3,
+          suggestions: [{
+            messageId: 'removeVar',
+            output: `
+        /* eslint react/jsx-uses-vars: 1 */
+        function Greetings() {
+          return <div />;
+        }
+        Greetings();
+      `,
+          }],
         },
       ],
     },
-  ]),
+  ].map((test) => {
+    if (!ruleNoUnusedVars.meta.hasSuggestions) {
+      test.errors = test.errors.map((error) => {
+        // https://github.com/eslint/eslint/pull/18352 added suggestions to no-unused-vars in eslint v9.17.0
+        delete error.suggestions;
+        return error;
+      });
+    }
+    return test;
+  })),
 });
 
 // Check compatibility with eslint prefer-const rule (#716)
diff --git tests/lib/rules/no-danger.js tests/lib/rules/no-danger.js
index e354ad28bd..9403a220bf 100644
--- tests/lib/rules/no-danger.js
+++ tests/lib/rules/no-danger.js
@@ -129,5 +129,49 @@ ruleTester.run('no-danger', rule, {
         },
       ],
     },
+    {
+      code: `
+        import type { ComponentProps } from "react";
+
+        const Comp = "div";
+        const Component = () => <></>;
+
+        const NestedComponent = (_props: ComponentProps<"div">) => <></>;
+
+        Component.NestedComponent = NestedComponent;
+
+        function App() {
+          return (
+            <>
+              <div dangerouslySetInnerHTML={{ __html: "<div>aaa</div>" }} />
+              <Comp dangerouslySetInnerHTML={{ __html: "<div>aaa</div>" }} />
+
+              <Component.NestedComponent
+                dangerouslySetInnerHTML={{ __html: '<div>aaa</div>' }}
+              />
+            </>
+          );
+        }
+      `,
+      features: ['fragment', 'types'],
+      options: [{ customComponentNames: ['*'] }],
+      errors: [
+        {
+          messageId: 'dangerousProp',
+          data: { name: 'dangerouslySetInnerHTML' },
+          line: 14,
+        },
+        {
+          messageId: 'dangerousProp',
+          data: { name: 'dangerouslySetInnerHTML' },
+          line: 15,
+        },
+        {
+          messageId: 'dangerousProp',
+          data: { name: 'dangerouslySetInnerHTML' },
+          line: 18,
+        },
+      ],
+    },
   ]),
 });
diff --git tests/lib/rules/no-unknown-property.js tests/lib/rules/no-unknown-property.js
index 32e6c6d6b3..97ae42ea57 100644
--- tests/lib/rules/no-unknown-property.js
+++ tests/lib/rules/no-unknown-property.js
@@ -89,6 +89,12 @@ ruleTester.run('no-unknown-property', rule, {
     { code: '<div onPointerDown={this.onDown} onPointerUp={this.onUp} />' },
     { code: '<input type="checkbox" defaultChecked={this.state.checkbox} />' },
     { code: '<div onTouchStart={this.startAnimation} onTouchEnd={this.stopAnimation} onTouchCancel={this.cancel} onTouchMove={this.move} onMouseMoveCapture={this.capture} onTouchCancelCapture={this.log} />' },
+    {
+      code: '<link precedence="medium" href="https://foo.bar" rel="canonical" />',
+      settings: {
+        react: { version: '19.0.0' },
+      },
+    },
     // Case ignored attributes, for `charset` discussion see https://github.com/jsx-eslint/eslint-plugin-react/pull/1863
     { code: '<meta charset="utf-8" />;' },
     { code: '<meta charSet="utf-8" />;' },
diff --git tests/lib/rules/prop-types.js tests/lib/rules/prop-types.js
index 39dc8bad3c..4a7d05ad6f 100644
--- tests/lib/rules/prop-types.js
+++ tests/lib/rules/prop-types.js
@@ -4117,6 +4117,470 @@ ruleTester.run('prop-types', rule, {
       `,
       features: ['ts', 'no-babel'],
     },
+    {
+      code: `
+        import {ComponentPropsWithoutRef, forwardRef} from "react";
+
+        export const FancyButton = forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import {ComponentProps, forwardRef} from "react";
+
+        export const FancyButton = forwardRef<HTMLButtonElement, ComponentProps<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import {ComponentPropsWithoutRef, ElementRef, forwardRef} from "react";
+
+        const BaseButton = forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = forwardRef<ElementRef<typeof BaseButton>, ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import {ComponentProps, ElementRef, forwardRef} from "react";
+
+        const BaseButton = forwardRef<HTMLButtonElement, ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = forwardRef<ElementRef<typeof BaseButton>, ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import {ComponentProps, ComponentPropsWithoutRef, ElementRef, forwardRef} from "react";
+
+        const BaseButton = forwardRef<HTMLButtonElement, ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = forwardRef<ElementRef<typeof BaseButton>, ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import {ComponentProps, ComponentPropsWithoutRef, ElementRef, forwardRef} from "react";
+
+        const BaseButton = forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = forwardRef<ElementRef<typeof BaseButton>, ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React from "react";
+
+        export const FancyButton = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React from "react";
+
+        export const FancyButton = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import * as React from "react";
+
+        export const FancyButton = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import * as React from "react";
+
+        export const FancyButton = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import * as React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import * as React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import * as React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import * as React from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, React.ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<React.ElementRef<typeof BaseButton>, React.ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React, {ComponentPropsWithoutRef} from "react";
+
+        export const FancyButton = React.forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React, {ComponentProps} from "react";
+
+        export const FancyButton = React.forwardRef<HTMLButtonElement, ComponentProps<"button">>(
+          ({ className, children, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+              {children}
+            </button>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React, {ComponentPropsWithoutRef, ElementRef} from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<ElementRef<typeof BaseButton>, ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React, {ComponentProps, ElementRef} from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<ElementRef<typeof BaseButton>, ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React, {ComponentProps, ComponentPropsWithoutRef, ElementRef} from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, ComponentPropsWithoutRef<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<ElementRef<typeof BaseButton>, ComponentProps<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
+    {
+      code: `
+        import React, {ComponentProps, ComponentPropsWithoutRef, ElementRef} from "react";
+
+        const BaseButton = React.forwardRef<HTMLButtonElement, ComponentProps<"button">>(
+          ({ children, className, ...props }, ref) => (
+            <button ref={ref} className={className} {...props}>
+                {children}
+            </button>
+          ),
+        );
+
+        export const FancyButton = React.forwardRef<ElementRef<typeof BaseButton>, ComponentPropsWithoutRef<typeof BaseButton>>(
+          ({ children, className, ...props }, ref) => (
+            <BaseButton ref={ref} className={className} {...props}>
+              {children}
+            </BaseButton>
+          ),
+        );
+      `,
+      features: ['ts', 'no-babel'],
+    },
     {
       code: `
         import React, { forwardRef } from 'react';
diff --git tsconfig.json tsconfig.json
index 39187b7f32..8772fab53d 100644
--- tsconfig.json
+++ tsconfig.json
@@ -19,5 +19,5 @@
     "alwaysStrict": false,                 /* Parse in strict mode and emit "use strict" for each source file. */
     "resolveJsonModule": true
   },
-  "include": ["lib"],
+  "include": ["lib", "types"],
 }
diff --git a/types/rules/jsx-no-literals.d.ts b/types/rules/jsx-no-literals.d.ts
new file mode 100644
index 0000000000..c1b9638958
--- /dev/null
+++ types/rules/jsx-no-literals.d.ts
@@ -0,0 +1,50 @@
+type RawElementConfig = {
+  noStrings?: boolean;
+  allowedStrings?: string[];
+  ignoreProps?: boolean;
+  noAttributeStrings?: boolean;
+};
+
+type RawOverrideConfig = {
+  allowElement?: boolean;
+  applyToNestedElements?: boolean;
+};
+
+interface RawElementOverrides {
+  elementOverrides?: Record<string, RawOverrideConfig>;
+}
+
+export type RawConfig = RawElementConfig & RawElementOverrides;
+
+interface ElementConfigType {
+  type: 'element';
+}
+
+interface ElementConfigProperties {
+  noStrings: boolean;
+  allowedStrings: Set<string>;
+  ignoreProps: boolean;
+  noAttributeStrings: boolean;
+}
+
+interface OverrideConfigProperties {
+  type: 'override';
+  name: string;
+  allowElement: boolean;
+  applyToNestedElements: boolean;
+}
+
+export type ElementConfig = {
+  type: 'element';
+} & ElementConfigProperties;
+
+export type OverrideConfig = OverrideConfigProperties & ElementConfigProperties;
+
+interface ElementOverrides {
+  elementOverrides: Record<string, OverrideConfig>;
+}
+
+export type Config = ElementConfig & ElementOverrides;
+
+export type ResolvedConfig = Config | OverrideConfig;
+
diff --git types/string.prototype.repeat/index.d.ts types/string.prototype.repeat/index.d.ts
index f240d9301f..b2e5992712 100644
--- types/string.prototype.repeat/index.d.ts
+++ types/string.prototype.repeat/index.d.ts
@@ -1,3 +1,4 @@
 declare module 'string.prototype.repeat' {
-  export = typeof Function.call.bind(String.prototype.repeat);
+  function repeat(text: string, count: number): string;
+  export = repeat;
 }

Description

This PR introduces several changes across multiple files, including bug fixes, dependency updates, and improvements to the build and test processes. The changes primarily focus on enhancing the eslint-plugin-react package, addressing various issues, and updating configurations.

Changes

Changes

  1. .eslintrc:

    • Added **/*/*.d.ts to the ignored files list.
  2. .github/workflows/node-18+.yml and .github/workflows/node-minors.yml:

    • Updated TypeScript-ESLint parser installation to use version 8.17 for Node 18+ and ESLint 8+.
  3. .github/workflows/npm-publish.yml and .github/workflows/release.yml:

    • Updated the step-security/harden-runner action to v2.
    • Added more allowed endpoints for egress policy.
  4. .github/workflows/type-check.yml:

    • Improved the type-checking process by packing the library and installing it in the test-published-types directory.
  5. CHANGELOG.md:

    • Added entries for version 7.37.3 with various bug fixes and documentation updates.
  6. index.js:

    • Replaced magic numbers with named constants for rule severity levels.
  7. Various rule files:

    • Fixed TypeScript-related issues and improved type annotations.
  8. lib/rules/no-danger.js:

    • Enhanced the rule to support nested component names.
  9. lib/rules/no-unknown-property.js:

    • Added support for the precedence prop in React 19.
  10. lib/util/propTypes.js:

    • Added support for ComponentProps and ComponentPropsWithoutRef generic types.
  11. package.json:

    • Updated version to 7.37.3.
    • Updated various dependencies.
  12. test-published-types/:

    • Updated configuration for type-checking tests.
  13. tests/:

    • Added and updated various test cases.
  14. tsconfig.json:

    • Included the types directory in the TypeScript compilation.
  15. Added new type definition files in the types/ directory.

sequenceDiagram
    participant Dev as Developer
    participant CI as CI/CD Pipeline
    participant NPM as NPM Registry
    participant Users as Package Users

    Dev->>CI: Push changes
    CI->>CI: Run tests and type checks
    CI->>CI: Build package
    CI->>NPM: Publish new version (7.37.3)
    NPM->>Users: Make new version available
    Users->>NPM: Install/update package
Loading

Possible Issues

  • The changes to the workflows might require additional testing to ensure CI/CD processes are not affected.
  • The addition of new type definitions and changes to existing ones may require updates to projects depending on this package.

Security Hotspots

No significant security hotspots were identified in this change.

This sequence diagram illustrates the flow of changes from development to end-users, highlighting the key steps in the release process for the new version 7.37.3 of eslint-plugin-react.

@renovate renovate bot force-pushed the renovate/all-minor-patch branch from 7b72901 to 3aa964f Compare January 2, 2025 18:05
mrose17 added a commit that referenced this pull request Jan 2, 2025
Looks good on desktop.
mrose17 added a commit that referenced this pull request Jan 2, 2025
Looks good on desktop and `dev1`.
mrose17 added a commit that referenced this pull request Jan 2, 2025
Looks good on desktop, `dev1`, and `dev2`.
Copy link
Member

@mrose17 mrose17 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good on desktop, dev1, dev2, and dev3.

@mrose17 mrose17 merged commit 1bc302e into main Jan 2, 2025
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependencies Pull requests that update a dependency file puLL-Merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant