-
Notifications
You must be signed in to change notification settings - Fork 35
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
Conversation
847216b
to
0123ab7
Compare
0123ab7
to
7b72901
Compare
[puLL-Merge] - jsx-eslint/[email protected] Diffdiff --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;
}
DescriptionThis 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. ChangesChanges
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
Possible Issues
Security HotspotsNo 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. |
7b72901
to
3aa964f
Compare
There was a problem hiding this 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
.
This PR contains the following updates:
7.37.2
->7.37.3
15.2.11
->15.3.0
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)no-unknown-property
][no-unknown-property]: supportprecedence
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
jsx-no-script-url
][jsx-no-script-url]: Improve tests ([#3849][] @radu2147)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.
This PR was generated by Mend Renovate. View the repository job log.