From 6dc7803acecac6e79d71d9619665403fe3073d79 Mon Sep 17 00:00:00 2001 From: akulsr0 Date: Tue, 18 Jun 2024 18:56:03 +0530 Subject: [PATCH 001/127] [Fix] `jsx-key`: incorrect behavior for checkKeyMustBeforeSpread with map callbacks --- CHANGELOG.md | 2 ++ lib/rules/jsx-key.js | 42 ++++++++++++++++++++++---------------- tests/lib/rules/jsx-key.js | 15 ++++++++++++++ 3 files changed, 41 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd3540427d..46cf73592b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,9 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`boolean-prop-naming`]: avoid a crash with a spread prop ([#3733][] @ljharb) * [`jsx-boolean-value`]: `assumeUndefinedIsFalse` with `never` must not allow explicit `true` value ([#3757][] @6uliver) * [`no-object-type-as-default-prop`]: enable rule for components with many parameters ([#3768][] @JulienR1) +* [`jsx-key`]: incorrect behavior for checkKeyMustBeforeSpread with map callbacks ([#3769][] @akulsr0) +[#3769]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3769 [#3768]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3768 [#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762 [#3757]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3757 diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index feee7ad5a0..825d21f4bb 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -73,11 +73,31 @@ module.exports = { const reactPragma = pragmaUtil.getFromContext(context); const fragmentPragma = pragmaUtil.getFragmentFromContext(context); + function isKeyAfterSpread(attributes) { + let hasFoundSpread = false; + return attributes.some((attribute) => { + if (attribute.type === 'JSXSpreadAttribute') { + hasFoundSpread = true; + return false; + } + if (attribute.type !== 'JSXAttribute') { + return false; + } + return hasFoundSpread && propName(attribute) === 'key'; + }); + } + function checkIteratorElement(node) { - if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) { - report(context, messages.missingIterKey, 'missingIterKey', { - node, - }); + if (node.type === 'JSXElement') { + if (!hasProp(node.openingElement.attributes, 'key')) { + report(context, messages.missingIterKey, 'missingIterKey', { node }); + } else { + const attrs = node.openingElement.attributes; + + if (checkKeyMustBeforeSpread && isKeyAfterSpread(attrs)) { + report(context, messages.keyBeforeSpread, 'keyBeforeSpread', { node }); + } + } } else if (checkFragmentShorthand && node.type === 'JSXFragment') { report(context, messages.missingIterKeyUsePrag, 'missingIterKeyUsePrag', { node, @@ -115,20 +135,6 @@ module.exports = { return returnStatements; } - function isKeyAfterSpread(attributes) { - let hasFoundSpread = false; - return attributes.some((attribute) => { - if (attribute.type === 'JSXSpreadAttribute') { - hasFoundSpread = true; - return false; - } - if (attribute.type !== 'JSXAttribute') { - return false; - } - return hasFoundSpread && propName(attribute) === 'key'; - }); - } - /** * Checks if the given node is a function expression or arrow function, * and checks if there is a missing key prop in return statement's arguments diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 17109e4215..748e9a2fd9 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -409,5 +409,20 @@ ruleTester.run('jsx-key', rule, { { messageId: 'missingIterKey' }, ], }, + { + code: ` + const TestCase = () => { + const list = [1, 2, 3, 4, 5]; + + return ( +
+ {list.map(x =>
)} +
+ ); + }; + `, + options: [{ checkKeyMustBeforeSpread: true }], + errors: [{ messageId: 'keyBeforeSpread' }], + }, ]), }); From 7d16666058e2b06193f08342c8b1f20a5468cf20 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 18 Jun 2024 12:44:51 -0700 Subject: [PATCH 002/127] [Dev Deps] update `@babel/core`, `@babel/eslint-parser`, `@babel/plugin-syntax-decorators`, `@babel/plugin-syntax-do-expressions`, `@babel/plugin-syntax-function-bind`, `@babel/preset-react`, `eslint-doc-generator`, `eslint-remote-tester-repositories` --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 4f5155c832..e004c4fb91 100644 --- a/package.json +++ b/package.json @@ -45,12 +45,12 @@ "string.prototype.matchall": "^4.0.11" }, "devDependencies": { - "@babel/core": "^7.24.6", - "@babel/eslint-parser": "^7.24.6", - "@babel/plugin-syntax-decorators": "^7.24.6", - "@babel/plugin-syntax-do-expressions": "^7.24.6", - "@babel/plugin-syntax-function-bind": "^7.24.6", - "@babel/preset-react": "^7.24.6", + "@babel/core": "^7.24.7", + "@babel/eslint-parser": "^7.24.7", + "@babel/plugin-syntax-decorators": "^7.24.7", + "@babel/plugin-syntax-do-expressions": "^7.24.7", + "@babel/plugin-syntax-function-bind": "^7.24.7", + "@babel/preset-react": "^7.24.7", "@types/eslint": "=7.2.10", "@types/estree": "0.0.52", "@types/node": "^4.9.5", @@ -59,11 +59,11 @@ "babel-eslint": "^8 || ^9 || ^10.1.0", "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint-config-airbnb-base": "^15.0.0", - "eslint-doc-generator": "^1.7.0", + "eslint-doc-generator": "^1.7.1", "eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1 || ^5.0.5", "eslint-plugin-import": "^2.29.1", "eslint-remote-tester": "^3.0.1", - "eslint-remote-tester-repositories": "^1.0.1", + "eslint-remote-tester-repositories": "^2.0.0", "eslint-scope": "^3.7.3", "espree": "^3.5.4", "glob": "=10.3.7", From eb56061c95d3e5e234d2f33def724c17aedc752e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 18 Jun 2024 12:45:35 -0700 Subject: [PATCH 003/127] [Deps] update `array.prototype.tosorted` --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e004c4fb91..c6bec01eff 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.2", "array.prototype.toreversed": "^1.1.2", - "array.prototype.tosorted": "^1.1.3", + "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "estraverse": "^5.3.0", From cef8123ff92460ed2369d670e8c7b9539e5994bd Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 18 Jun 2024 12:48:05 -0700 Subject: [PATCH 004/127] Update CHANGELOG and bump version --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46cf73592b..42e7d214f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +## [7.34.3] - 2024.06.18 + ### Fixed * [`prop-types`]: null-check rootNode before calling getScope ([#3762][] @crnhrv) * [`boolean-prop-naming`]: avoid a crash with a spread prop ([#3733][] @ljharb) @@ -12,6 +14,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange * [`no-object-type-as-default-prop`]: enable rule for components with many parameters ([#3768][] @JulienR1) * [`jsx-key`]: incorrect behavior for checkKeyMustBeforeSpread with map callbacks ([#3769][] @akulsr0) +[7.34.3]: https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.2...v7.34.3 [#3769]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3769 [#3768]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3768 [#3762]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3762 diff --git a/package.json b/package.json index c6bec01eff..ba818e4eec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-react", - "version": "7.34.2", + "version": "7.34.3", "author": "Yannick Croissant ", "description": "React specific linting rules for ESLint", "main": "index.js", From b7474504fe5b9101dd7f607d9bc71aa2e61dfb37 Mon Sep 17 00:00:00 2001 From: akulsr0 Date: Mon, 6 May 2024 14:13:56 +0530 Subject: [PATCH 005/127] [Fix] `prop-types`: fix `className` missing in prop validation false negative --- CHANGELOG.md | 6 ++++++ lib/util/propTypes.js | 19 +++++++++++++++++++ tests/lib/rules/prop-types.js | 23 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e7d214f0..5db11b6487 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ## Unreleased +### Fixed + +* [`prop-types`]: fix `className` missing in prop validation false negative ([#3749] @akulsr0) + +[#3749]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3749 + ## [7.34.3] - 2024.06.18 ### Fixed diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index 22063afb8e..84f5479f15 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -645,6 +645,17 @@ module.exports = function propTypesInstructions(context, components, utils) { this.shouldSpecifyOptionalChildrenProps = true; const rightMostName = getRightMostTypeName(node.typeName); + if ( + leftMostName === 'React' + && ( + rightMostName === 'HTMLAttributes' + || rightMostName === 'HTMLElement' + || rightMostName === 'HTMLProps' + ) + ) { + this.shouldSpecifyClassNameProp = true; + } + const importedName = localToImportedMap[rightMostName]; const idx = genericTypeParamIndexWherePropsArePresent[ leftMostName !== rightMostName ? rightMostName : importedName @@ -835,6 +846,14 @@ module.exports = function propTypesInstructions(context, components, utils) { isRequired: false, }; } + if (this.shouldSpecifyClassNameProp) { + this.declaredPropTypes.className = { + fullName: 'className', + name: 'className', + isRequired: false, + }; + } + this.foundDeclaredPropertiesList.forEach((tsInterfaceBody) => { if (tsInterfaceBody && (tsInterfaceBody.type === 'TSPropertySignature' || tsInterfaceBody.type === 'TSMethodSignature')) { let accessor = 'name'; diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js index 97206b7d44..638861e2bb 100644 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -4195,6 +4195,29 @@ ruleTester.run('prop-types', rule, { ); `, features: ['types'], + }, + { + code: ` + import React from "react" + + export function Heading({ className, ...props }: React.HTMLAttributes) { + return
+ } + `, + features: ['types'], + }, + { + code: ` + import React from 'react'; + type TDelIconProps = React.HTMLProps; + + const DelIcon: React.FC = ({className, ...rest}) => ( +
+ +
+ ); + `, + features: ['types'], } )), From 3c1d5203438965b3999911520a930306f6e9c58f Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 23 Jun 2024 23:00:32 -0700 Subject: [PATCH 006/127] [Dev Deps] downgrade `eslint-remote-tester-repositories` See https://github.com/jsx-eslint/eslint-plugin-react/issues/3632#issuecomment-2184859577 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ba818e4eec..6446e53be2 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "eslint-plugin-eslint-plugin": "^2.3.0 || ^3.5.3 || ^4.0.1 || ^5.0.5", "eslint-plugin-import": "^2.29.1", "eslint-remote-tester": "^3.0.1", - "eslint-remote-tester-repositories": "^2.0.0", + "eslint-remote-tester-repositories": "^1.0.1", "eslint-scope": "^3.7.3", "espree": "^3.5.4", "glob": "=10.3.7", From eda77f32be2bb495ad7bc473da99323d025c91ec Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 3 Jul 2024 23:03:37 -0500 Subject: [PATCH 007/127] [Refactor] avoid making a holey array --- lib/rules/jsx-closing-bracket-location.js | 8 +++++--- lib/rules/jsx-closing-tag-location.js | 4 +++- lib/rules/jsx-indent-props.js | 5 ++++- lib/rules/jsx-indent.js | 3 ++- package.json | 6 ++++-- types/string.prototype.repeat/index.d.ts | 3 +++ 6 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 types/string.prototype.repeat/index.d.ts diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js index 26556d6117..bb4658218a 100644 --- a/lib/rules/jsx-closing-bracket-location.js +++ b/lib/rules/jsx-closing-bracket-location.js @@ -6,6 +6,8 @@ 'use strict'; const has = require('object.hasown/polyfill')(); +const repeat = require('string.prototype.repeat'); + const docsUrl = require('../util/docsUrl'); const getSourceCode = require('../util/eslint').getSourceCode; const report = require('../util/report'); @@ -168,7 +170,7 @@ module.exports = { function getIndentation(tokens, expectedLocation, correctColumn) { const newColumn = correctColumn || 0; let indentation; - let spaces = []; + let spaces = ''; switch (expectedLocation) { case 'props-aligned': indentation = /^\s*/.exec(getSourceCode(context).lines[tokens.lastProp.firstLine - 1])[0]; @@ -182,9 +184,9 @@ module.exports = { } if (indentation.length + 1 < newColumn) { // Non-whitespace characters were included in the column offset - spaces = new Array(+correctColumn + 1 - indentation.length); + spaces = repeat(' ', +correctColumn - indentation.length); } - return indentation + spaces.join(' '); + return indentation + spaces; } /** diff --git a/lib/rules/jsx-closing-tag-location.js b/lib/rules/jsx-closing-tag-location.js index 5d5a95cb7b..a2828d12ae 100644 --- a/lib/rules/jsx-closing-tag-location.js +++ b/lib/rules/jsx-closing-tag-location.js @@ -5,6 +5,8 @@ 'use strict'; +const repeat = require('string.prototype.repeat'); + const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); @@ -53,7 +55,7 @@ module.exports = { node, loc: node.loc, fix(fixer) { - const indent = Array(opening.loc.start.column + 1).join(' '); + const indent = repeat(' ', opening.loc.start.column); if (astUtil.isNodeFirstInLine(context, node)) { return fixer.replaceTextRange( [node.range[0] - node.loc.start.column, node.range[0]], diff --git a/lib/rules/jsx-indent-props.js b/lib/rules/jsx-indent-props.js index 050e721df3..10adb1b8d9 100644 --- a/lib/rules/jsx-indent-props.js +++ b/lib/rules/jsx-indent-props.js @@ -30,6 +30,8 @@ 'use strict'; +const repeat = require('string.prototype.repeat'); + const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const getText = require('../util/eslint').getText; @@ -130,7 +132,8 @@ module.exports = { data: msgContext, fix(fixer) { return fixer.replaceTextRange([node.range[0] - node.loc.start.column, node.range[0]], - Array(needed + 1).join(indentType === 'space' ? ' ' : '\t')); + repeat(indentType === 'space' ? ' ' : '\t', needed) + ); }, }); } diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js index 27397172c8..0bfd0507dd 100644 --- a/lib/rules/jsx-indent.js +++ b/lib/rules/jsx-indent.js @@ -31,6 +31,7 @@ 'use strict'; const matchAll = require('string.prototype.matchall'); +const repeat = require('string.prototype.repeat'); const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); @@ -109,7 +110,7 @@ module.exports = { * @private */ function getFixerFunction(node, needed) { - const indent = Array(needed + 1).join(indentChar); + const indent = repeat(indentChar, needed); if (node.type === 'JSXText' || node.type === 'Literal') { return function fix(fixer) { diff --git a/package.json b/package.json index 6446e53be2..570331196c 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,8 @@ "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.11", + "string.prototype.repeat": "^1.0.0" }, "devDependencies": { "@babel/core": "^7.24.7", @@ -103,7 +104,8 @@ ".eslintrc", ".editorconfig", "tsconfig.json", - ".markdownlint*" + ".markdownlint*", + "types" ] } } diff --git a/types/string.prototype.repeat/index.d.ts b/types/string.prototype.repeat/index.d.ts new file mode 100644 index 0000000000..f240d9301f --- /dev/null +++ b/types/string.prototype.repeat/index.d.ts @@ -0,0 +1,3 @@ +declare module 'string.prototype.repeat' { + export = typeof Function.call.bind(String.prototype.repeat); +} From bcb987a67b36d160ad0daa1dcc9ef384b4d5a73c Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 3 Jul 2024 23:04:29 -0500 Subject: [PATCH 008/127] [Refactor] use `hasown` instead of the larger `object.hasown` --- lib/rules/forbid-elements.js | 2 +- lib/rules/jsx-closing-bracket-location.js | 2 +- lib/rules/jsx-curly-spacing.js | 2 +- lib/rules/jsx-max-depth.js | 2 +- lib/rules/jsx-no-duplicate-props.js | 2 +- lib/rules/jsx-wrap-multilines.js | 2 +- lib/rules/no-array-index-key.js | 2 +- lib/rules/no-danger.js | 2 +- lib/rules/no-unknown-property.js | 2 +- lib/rules/sort-comp.js | 2 +- lib/rules/void-dom-elements-no-children.js | 2 +- package.json | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/rules/forbid-elements.js b/lib/rules/forbid-elements.js index dd3ffa8f5d..4ad66eebe0 100644 --- a/lib/rules/forbid-elements.js +++ b/lib/rules/forbid-elements.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const docsUrl = require('../util/docsUrl'); const getText = require('../util/eslint').getText; const isCreateElement = require('../util/isCreateElement'); diff --git a/lib/rules/jsx-closing-bracket-location.js b/lib/rules/jsx-closing-bracket-location.js index bb4658218a..21f99ffc9c 100644 --- a/lib/rules/jsx-closing-bracket-location.js +++ b/lib/rules/jsx-closing-bracket-location.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const repeat = require('string.prototype.repeat'); const docsUrl = require('../util/docsUrl'); diff --git a/lib/rules/jsx-curly-spacing.js b/lib/rules/jsx-curly-spacing.js index 8d504f1d20..71bf901904 100644 --- a/lib/rules/jsx-curly-spacing.js +++ b/lib/rules/jsx-curly-spacing.js @@ -11,7 +11,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const docsUrl = require('../util/docsUrl'); const getSourceCode = require('../util/eslint').getSourceCode; const report = require('../util/report'); diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index 6b1db78189..be0f582264 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const includes = require('array-includes'); const variableUtil = require('../util/variable'); const jsxUtil = require('../util/jsx'); diff --git a/lib/rules/jsx-no-duplicate-props.js b/lib/rules/jsx-no-duplicate-props.js index cf7737698a..77de5bde51 100644 --- a/lib/rules/jsx-no-duplicate-props.js +++ b/lib/rules/jsx-no-duplicate-props.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); diff --git a/lib/rules/jsx-wrap-multilines.js b/lib/rules/jsx-wrap-multilines.js index 17a9812465..6f5ad50bbf 100644 --- a/lib/rules/jsx-wrap-multilines.js +++ b/lib/rules/jsx-wrap-multilines.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const docsUrl = require('../util/docsUrl'); const eslintUtil = require('../util/eslint'); const jsxUtil = require('../util/jsx'); diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index c79fd56e2c..47b8f47b57 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const astUtil = require('../util/ast'); const docsUrl = require('../util/docsUrl'); const pragma = require('../util/pragma'); diff --git a/lib/rules/no-danger.js b/lib/rules/no-danger.js index 1cb0273739..eecdd87e23 100644 --- a/lib/rules/no-danger.js +++ b/lib/rules/no-danger.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const fromEntries = require('object.fromentries/polyfill')(); const docsUrl = require('../util/docsUrl'); diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js index 3e8c6de111..95359d10fd 100644 --- a/lib/rules/no-unknown-property.js +++ b/lib/rules/no-unknown-property.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const docsUrl = require('../util/docsUrl'); const getText = require('../util/eslint').getText; const testReactVersion = require('../util/version').testReactVersion; diff --git a/lib/rules/sort-comp.js b/lib/rules/sort-comp.js index fb0bf8f614..cf24961629 100644 --- a/lib/rules/sort-comp.js +++ b/lib/rules/sort-comp.js @@ -5,7 +5,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const entries = require('object.entries'); const values = require('object.values'); const arrayIncludes = require('array-includes'); diff --git a/lib/rules/void-dom-elements-no-children.js b/lib/rules/void-dom-elements-no-children.js index a8a6ba0149..66db2a8ad3 100644 --- a/lib/rules/void-dom-elements-no-children.js +++ b/lib/rules/void-dom-elements-no-children.js @@ -6,7 +6,7 @@ 'use strict'; -const has = require('object.hasown/polyfill')(); +const has = require('hasown'); const docsUrl = require('../util/docsUrl'); const isCreateElement = require('../util/isCreateElement'); diff --git a/package.json b/package.json index 570331196c..259b8f0e97 100644 --- a/package.json +++ b/package.json @@ -33,11 +33,11 @@ "doctrine": "^2.1.0", "es-iterator-helpers": "^1.0.19", "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.hasown": "^1.1.4", "object.values": "^1.2.0", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", From f1ae6edc4092f58ee8ba46c6cefc19fa316fbffb Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 3 Jul 2024 23:13:31 -0500 Subject: [PATCH 009/127] [Refactor] `sort-comp`: use Object.entries instead of for-in --- lib/rules/sort-comp.js | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/rules/sort-comp.js b/lib/rules/sort-comp.js index cf24961629..e35574a682 100644 --- a/lib/rules/sort-comp.js +++ b/lib/rules/sort-comp.js @@ -124,6 +124,7 @@ module.exports = { }, create: Components.detect((context, components) => { + /** @satisfies {Record} */ const errors = {}; const methodsOrder = getMethodsOrder(context.options[0]); @@ -287,18 +288,19 @@ module.exports = { * Dedupe errors, only keep the ones with the highest score and delete the others */ function dedupeErrors() { - for (const i in errors) { - if (has(errors, i)) { - const index = errors[i].closest.ref.index; - if (errors[index]) { - if (errors[i].score > errors[index].score) { - delete errors[index]; - } else { - delete errors[i]; - } + entries(errors).forEach((entry) => { + const i = entry[0]; + const error = entry[1]; + + const index = error.closest.ref.index; + if (errors[index]) { + if (error.score > errors[index].score) { + delete errors[index]; + } else { + delete errors[i]; } } - } + }); } /** From 51d342ba350ae7d7dabce1caa648e71926ef283f Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Wed, 3 Jul 2024 23:18:32 -0500 Subject: [PATCH 010/127] [patch] make TS happy --- .eslintrc | 112 +++++++++++++++---------------- lib/rules/forbid-elements.js | 1 + lib/rules/no-array-index-key.js | 4 +- lib/rules/no-unknown-property.js | 6 +- 4 files changed, 63 insertions(+), 60 deletions(-) diff --git a/.eslintrc b/.eslintrc index d10247d296..e609aa23be 100644 --- a/.eslintrc +++ b/.eslintrc @@ -22,61 +22,61 @@ "object-shorthand": [2, "always", { "ignoreConstructors": false, "avoidQuotes": false, // this is the override vs airbnb - }], - "max-len": [2, 120, { - "ignoreStrings": true, - "ignoreTemplateLiterals": true, - "ignoreComments": true, - }], - "consistent-return": 0, + }], + "max-len": [2, 140, { + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreComments": true, + }], + "consistent-return": 0, - "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }], - "prefer-object-spread": 0, // until node 8 is required - "prefer-rest-params": 0, // until node 6 is required - "prefer-spread": 0, // until node 6 is required - "function-call-argument-newline": 1, // TODO: enable - "function-paren-newline": 0, - "no-plusplus": [2, {"allowForLoopAfterthoughts": true}], - "no-param-reassign": 1, - "no-restricted-syntax": [2, { - "selector": "ObjectPattern", - "message": "Object destructuring is not compatible with Node v4" - }], - "strict": [2, "safe"], - "valid-jsdoc": [2, { - "requireReturn": false, - "requireParamDescription": false, - "requireReturnDescription": false, - }], + "prefer-destructuring": [2, { "array": false, "object": false }, { "enforceForRenamedProperties": false }], + "prefer-object-spread": 0, // until node 8 is required + "prefer-rest-params": 0, // until node 6 is required + "prefer-spread": 0, // until node 6 is required + "function-call-argument-newline": 1, // TODO: enable + "function-paren-newline": 0, + "no-plusplus": [2, {"allowForLoopAfterthoughts": true}], + "no-param-reassign": 1, + "no-restricted-syntax": [2, { + "selector": "ObjectPattern", + "message": "Object destructuring is not compatible with Node v4" + }], + "strict": [2, "safe"], + "valid-jsdoc": [2, { + "requireReturn": false, + "requireParamDescription": false, + "requireReturnDescription": false, + }], - "eslint-plugin/consistent-output": 0, - "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }], - "eslint-plugin/require-meta-schema": 0, - "eslint-plugin/require-meta-type": 0 - }, - "overrides": [ - { - "files": "tests/**", - "rules": { - "no-template-curly-in-string": 1, - }, - }, - { - "files": "markdown.config.js", - "rules": { - "no-console": 0, - }, - }, - { - "files": ".github/workflows/*.js", - "parserOptions": { - "ecmaVersion": 2019, - }, - "rules": { - "camelcase": 0, - "no-console": 0, - "no-restricted-syntax": 0, - }, - }, - ], - } + "eslint-plugin/consistent-output": 0, + "eslint-plugin/require-meta-docs-description": [2, { "pattern": "^(Enforce|Require|Disallow)" }], + "eslint-plugin/require-meta-schema": 0, + "eslint-plugin/require-meta-type": 0 + }, + "overrides": [ + { + "files": "tests/**", + "rules": { + "no-template-curly-in-string": 1, + }, + }, + { + "files": "markdown.config.js", + "rules": { + "no-console": 0, + }, + }, + { + "files": ".github/workflows/*.js", + "parserOptions": { + "ecmaVersion": 2019, + }, + "rules": { + "camelcase": 0, + "no-console": 0, + "no-restricted-syntax": 0, + }, + }, + ], +} diff --git a/lib/rules/forbid-elements.js b/lib/rules/forbid-elements.js index 4ad66eebe0..6d672cf70d 100644 --- a/lib/rules/forbid-elements.js +++ b/lib/rules/forbid-elements.js @@ -60,6 +60,7 @@ module.exports = { const configuration = context.options[0] || {}; const forbidConfiguration = configuration.forbid || []; + /** @type {Record} */ const indexedForbidConfigs = {}; forbidConfiguration.forEach((item) => { diff --git a/lib/rules/no-array-index-key.js b/lib/rules/no-array-index-key.js index 47b8f47b57..2a0ae41e6f 100644 --- a/lib/rules/no-array-index-key.js +++ b/lib/rules/no-array-index-key.js @@ -117,6 +117,8 @@ module.exports = { return null; } + const name = /** @type {keyof iteratorFunctionsToIndexParamPosition} */ (callee.property.name); + const callbackArg = isUsingReactChildren(node) ? node.arguments[1] : node.arguments[0]; @@ -131,7 +133,7 @@ module.exports = { const params = callbackArg.params; - const indexParamPosition = iteratorFunctionsToIndexParamPosition[callee.property.name]; + const indexParamPosition = iteratorFunctionsToIndexParamPosition[name]; if (params.length < indexParamPosition + 1) { return null; } diff --git a/lib/rules/no-unknown-property.js b/lib/rules/no-unknown-property.js index 95359d10fd..674e93071f 100644 --- a/lib/rules/no-unknown-property.js +++ b/lib/rules/no-unknown-property.js @@ -491,10 +491,10 @@ function tagNameHasDot(node) { */ function getStandardName(name, context) { if (has(DOM_ATTRIBUTE_NAMES, name)) { - return DOM_ATTRIBUTE_NAMES[name]; + return DOM_ATTRIBUTE_NAMES[/** @type {keyof DOM_ATTRIBUTE_NAMES} */ (name)]; } if (has(SVGDOM_ATTRIBUTE_NAMES, name)) { - return SVGDOM_ATTRIBUTE_NAMES[name]; + 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. @@ -592,7 +592,7 @@ module.exports = { // Let's dive deeper into tags that are HTML/DOM elements (`