diff --git a/.github/workflows/node-18+.yml b/.github/workflows/node-18+.yml index 7dcb4daff5..ee7d126939 100644 --- a/.github/workflows/node-18+.yml +++ b/.github/workflows/node-18+.yml @@ -38,6 +38,64 @@ jobs: - 10 - 9 - 8 + typescript-eslint: + - 5 + - 6 + - 7 + - 8 + exclude: + - eslint: 3 + babel-eslint: 10 + - eslint: 3 + typescript-eslint: 5 + - eslint: 4 + typescript-eslint: 5 + - eslint: 4.14 + typescript-eslint: 5 + - eslint: 5 + typescript-eslint: 5 + - eslint: 3 + typescript-eslint: 6 + - eslint: 4 + typescript-eslint: 6 + - eslint: 4.14 + typescript-eslint: 6 + - eslint: 5 + typescript-eslint: 6 + - eslint: 6 + typescript-eslint: 6 + - eslint: 9 + typescript-eslint: 6 + - eslint: 3 + typescript-eslint: 7 + - eslint: 4 + typescript-eslint: 7 + - eslint: 4.14 + typescript-eslint: 7 + - eslint: 5 + typescript-eslint: 7 + - eslint: 6 + typescript-eslint: 7 + - eslint: 7 + typescript-eslint: 7 + - eslint: 9 + typescript-eslint: 7 + - eslint: 3 + typescript-eslint: 8 + - eslint: 4 + typescript-eslint: 8 + - eslint: 4.14 + typescript-eslint: 8 + - eslint: 5 + typescript-eslint: 8 + - eslint: 6 + typescript-eslint: 8 + - eslint: 7 + typescript-eslint: 8 + - eslint: 9 + typescript-eslint: 5 + - node-version: 19 + typescript-eslint: 7 steps: - uses: actions/checkout@v4 @@ -46,9 +104,9 @@ jobs: with: node-version: ${{ matrix.node-version }} after_install: | - npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@5" "babel-eslint@${{ matrix.babel-eslint }}" + npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@${{ matrix.typescript-eslint }}" "babel-eslint@${{ matrix.babel-eslint }}" env: - NPM_CONFIG_LEGACY_PEER_DEPS: true + NPM_CONFIG_LEGACY_PEER_DEPS: "${{ matrix.typescript-eslint >= 6 && 'false' || 'true' }}" - run: npx ls-engines - run: npm run unit-test - uses: codecov/codecov-action@v3.1.5 diff --git a/.github/workflows/node-minors.yml b/.github/workflows/node-minors.yml index 548b571dd2..69f92684ea 100644 --- a/.github/workflows/node-minors.yml +++ b/.github/workflows/node-minors.yml @@ -100,10 +100,10 @@ jobs: with: node-version: ${{ matrix.node-version }} after_install: | - npm install --no-save "eslint@${{ matrix.eslint }}" "@typescript-eslint/parser@${{ 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' || (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: true + NPM_CONFIG_LEGACY_PEER_DEPS: "${{ matrix.node-version >= 16 && matrix.eslint >= 7 && 'false' || 'true' }}" - run: npx ls-engines if: ${{ matrix.node-version >= 12 }} - run: npm run unit-test diff --git a/CHANGELOG.md b/CHANGELOG.md index 63bc11a1f8..12c9e1f850 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Change Log + All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](https://keepachangelog.com). @@ -8,6 +9,14 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange ### Added * [`no-string-refs`]: allow this.refs in > 18.3.0 ([#3807][] @henryqdineen) +### Fixed +* [`function-component-definition`], [`boolean-prop-naming`], [`jsx-first-prop-new-line`], [`jsx-props-no-multi-spaces`], `propTypes`: use type args ([#3629][] @HenryBrown0) + +### Changed +* [Tests] add @typescript-eslint/parser v6 ([#3629][] @HenryBrown0) +* [Tests] add @typescript-eslint/parser v7 and v8 ([#3629][] @hampustagerud) + +[#3629]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3629 [#3807]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3807 ## [7.35.2] - 2024.09.03 diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index 27becc4525..afa844a903 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -259,14 +259,16 @@ module.exports = { return; } - const annotationTypeParams = component.node.parent.id.typeAnnotation.typeAnnotation.typeParameters; + const annotationTypeArguments = propsUtil.getTypeArguments( + component.node.parent.id.typeAnnotation.typeAnnotation + ); if ( - annotationTypeParams && ( - annotationTypeParams.type === 'TSTypeParameterInstantiation' - || annotationTypeParams.type === 'TypeParameterInstantiation' + annotationTypeArguments && ( + annotationTypeArguments.type === 'TSTypeParameterInstantiation' + || annotationTypeArguments.type === 'TypeParameterInstantiation' ) ) { - return annotationTypeParams.params.find( + return annotationTypeArguments.params.find( (param) => param.type === 'TSTypeReference' || param.type === 'GenericTypeAnnotation' ); } diff --git a/lib/rules/function-component-definition.js b/lib/rules/function-component-definition.js index 24ad3f48d7..cca665abe2 100644 --- a/lib/rules/function-component-definition.js +++ b/lib/rules/function-component-definition.js @@ -10,6 +10,7 @@ const Components = require('../util/Components'); const docsUrl = require('../util/docsUrl'); const reportC = require('../util/report'); const getText = require('../util/eslint').getText; +const propsUtil = require('../util/props'); // ------------------------------------------------------------------------------ // Rule Definition @@ -34,12 +35,12 @@ const UNNAMED_FUNCTION_TEMPLATES = { }; function hasOneUnconstrainedTypeParam(node) { - const nodeTypeParams = node.typeParameters; + const nodeTypeArguments = propsUtil.getTypeArguments(node); - return nodeTypeParams - && nodeTypeParams.params - && nodeTypeParams.params.length === 1 - && !nodeTypeParams.params[0].constraint; + return nodeTypeArguments + && nodeTypeArguments.params + && nodeTypeArguments.params.length === 1 + && !nodeTypeArguments.params[0].constraint; } function hasName(node) { @@ -202,11 +203,12 @@ module.exports = { varType = node.parent.parent.kind; } + const nodeTypeArguments = propsUtil.getTypeArguments(node); return (fixer) => fixer.replaceTextRange( options.range, buildFunction(options.template, { typeAnnotation, - typeParams: getNodeText(node.typeParameters, source), + typeParams: getNodeText(nodeTypeArguments, source), params: getParams(node, source), returnType: getNodeText(node.returnType, source), body: getBody(node, source), diff --git a/lib/rules/jsx-first-prop-new-line.js b/lib/rules/jsx-first-prop-new-line.js index 7c9d969b51..3014d24f1e 100644 --- a/lib/rules/jsx-first-prop-new-line.js +++ b/lib/rules/jsx-first-prop-new-line.js @@ -7,6 +7,7 @@ const docsUrl = require('../util/docsUrl'); const report = require('../util/report'); +const propsUtil = require('../util/props'); // ------------------------------------------------------------------------------ // Rule Definition @@ -55,7 +56,8 @@ module.exports = { report(context, messages.propOnNewLine, 'propOnNewLine', { node: decl, fix(fixer) { - return fixer.replaceTextRange([(node.typeParameters || node.name).range[1], decl.range[0]], '\n'); + const nodeTypeArguments = propsUtil.getTypeArguments(node); + return fixer.replaceTextRange([(nodeTypeArguments || node.name).range[1], decl.range[0]], '\n'); }, }); } diff --git a/lib/rules/jsx-max-depth.js b/lib/rules/jsx-max-depth.js index 12206a4081..12625fa704 100644 --- a/lib/rules/jsx-max-depth.js +++ b/lib/rules/jsx-max-depth.js @@ -91,7 +91,7 @@ module.exports = { function findJSXElementOrFragment(startNode, name, previousReferences) { function find(refs, prevRefs) { for (let i = refs.length - 1; i >= 0; i--) { - if (has(refs[i], 'writeExpr')) { + if (typeof refs[i].writeExpr !== 'undefined') { const writeExpr = refs[i].writeExpr; return (jsxUtil.isJSX(writeExpr) diff --git a/lib/rules/jsx-props-no-multi-spaces.js b/lib/rules/jsx-props-no-multi-spaces.js index ce80338cd8..8402c6d4ff 100644 --- a/lib/rules/jsx-props-no-multi-spaces.js +++ b/lib/rules/jsx-props-no-multi-spaces.js @@ -8,6 +8,7 @@ const docsUrl = require('../util/docsUrl'); const eslintUtil = require('../util/eslint'); const report = require('../util/report'); +const propsUtil = require('../util/props'); const getSourceCode = eslintUtil.getSourceCode; const getText = eslintUtil.getText; @@ -103,18 +104,18 @@ module.exports = { } function containsGenericType(node) { - const nodeTypeParams = node.typeParameters; - if (typeof nodeTypeParams === 'undefined') { + const nodeTypeArguments = propsUtil.getTypeArguments(node); + if (typeof nodeTypeArguments === 'undefined') { return false; } - return nodeTypeParams.type === 'TSTypeParameterInstantiation'; + return nodeTypeArguments.type === 'TSTypeParameterInstantiation'; } function getGenericNode(node) { const name = node.name; if (containsGenericType(node)) { - const type = node.typeParameters; + const nodeTypeArguments = propsUtil.getTypeArguments(node); return Object.assign( {}, @@ -122,7 +123,7 @@ module.exports = { { range: [ name.range[0], - type.range[1], + nodeTypeArguments.range[1], ], } ); diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index a1dce37d8b..f26a6510c9 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -41,7 +41,8 @@ function isFunctionType(node) { */ function isSuperTypeParameterPropsDeclaration(node) { if (node && (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')) { - if (node.superTypeParameters && node.superTypeParameters.params.length > 0) { + const parameters = propsUtil.getSuperTypeArguments(node); + if (parameters && parameters.params.length > 0) { return true; } } @@ -639,8 +640,8 @@ module.exports = function propTypesInstructions(context, components, utils) { typeName = node.typeName.name; const leftMostName = getLeftMostTypeName(node.typeName); const shouldTraverseTypeParams = genericReactTypesImport.has(leftMostName); - const nodeTypeParams = node.typeParameters; - if (shouldTraverseTypeParams && nodeTypeParams && nodeTypeParams.length !== 0) { + const nodeTypeArguments = propsUtil.getTypeArguments(node); + if (shouldTraverseTypeParams && nodeTypeArguments && nodeTypeArguments.length !== 0) { // All react Generic types are derived from: // type PropsWithChildren
= P & { children?: ReactNode | undefined }
// So we should construct an optional children prop
@@ -662,7 +663,7 @@ module.exports = function propTypesInstructions(context, components, utils) {
const idx = genericTypeParamIndexWherePropsArePresent[
leftMostName !== rightMostName ? rightMostName : importedName
];
- const nextNode = nodeTypeParams.params[idx];
+ const nextNode = nodeTypeArguments.params[idx];
this.visitTSNode(nextNode);
return;
}
@@ -759,10 +760,10 @@ module.exports = function propTypesInstructions(context, components, utils) {
convertReturnTypeToPropTypes(node, rootNode) {
// ReturnType