Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin-react-hooks): convert to typescript and package type declarations #32240

Conversation

michaelfaith
Copy link
Contributor

@michaelfaith michaelfaith commented Jan 28, 2025

@michaelfaith
Copy link
Contributor Author

michaelfaith commented Jan 28, 2025

Core migration is done. Tests are green when run locally. Now working on integrating it into the existing build infra and developing e2e test fixtures.

Copy link
Member

@poteto poteto left a comment

Choose a reason for hiding this comment

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

Thanks for working on this! I think as it is the PR is bordering on being way too large to review thoroughly. It'd be good if we can split this up into a few smaller PRs. For example the peripheral changes (installing/updating packages) or script/babel/build changes could also be split.

I would also recommend not rewriting existing patterns like for loops to for..of and x == null to !x, since they have subtle differences. Keep the conversion as 1:1 as possible unless it's strictly necessary for type soundness. Additionally if you need to add new code (eg adding continue inside of a loop), could you add more context on why it's necessary?

Comment on lines 491 to 493
if (!referenceNode) {
continue;
}
Copy link
Member

Choose a reason for hiding this comment

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

Why is this needed?

Copy link
Contributor Author

@michaelfaith michaelfaith Jan 29, 2025

Choose a reason for hiding this comment

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

fastFindReferenceWithParent returns null if it's unable to find a suitable node, but all the logic below this assumes that it's returned a Node. In practice, it shouldn't really ever not return a node, so this is more of an edge case, but since the function isn't narrowing out null, this early return keeps the rest of the assumptions in tact.

@TTVLILSNAZ

This comment was marked as off-topic.

@michaelfaith
Copy link
Contributor Author

@poteto thanks for taking a look at this already! I've made some updates and responded to several of the feedback items.

It'd be good if we can split this up into a few smaller PRs. For example the peripheral changes (installing/updating packages) or script/babel/build changes could also be split.

I'm thinking these could be good candidates for breaking out as separate changes, but wanted to get your input:

  • removing the useEffectEvent / EXPERIMENTAL checks you mentioned
  • Add dependencies
  • Add tsconfig and the two d.ts files in the types directory

Thoughts?

Copy link
Member

@poteto poteto left a comment

Choose a reason for hiding this comment

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

Thanks for addressing the previous feedback!

@poteto
Copy link
Member

poteto commented Jan 30, 2025

@poteto thanks for taking a look at this already! I've made some updates and responded to several of the feedback items.

It'd be good if we can split this up into a few smaller PRs. For example the peripheral changes (installing/updating packages) or script/babel/build changes could also be split.

I'm thinking these could be good candidates for breaking out as separate changes, but wanted to get your input:

  • removing the useEffectEvent / EXPERIMENTAL checks you mentioned

  • Add dependencies

  • Add tsconfig and the two d.ts files in the types directory

Thoughts?

That split makes sense. I would also try to get the build changes done in an earlier PR, and this PR could be for just the changes to the rules themselves.

@michaelfaith
Copy link
Contributor Author

michaelfaith commented Jan 30, 2025

I would also try to get the build changes done in an earlier PR, and this PR could be for just the changes to the rules themselves.

Sounds good. I updated the description of this PR to include tasks for each of those, and will link PRs as I work through each.

michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 30, 2025
…useEffectEvent

Contributing to facebook#32240, this change removes the experimental `useEffectEvent` checks in the two react hooks eslint rule.  As the api is refined, and the compiler rule(s) are incorporated into this plugin, these checks will be covered then.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 30, 2025
…useEffectEvent

Contributing to facebook#32240, this change removes the experimental `useEffectEvent` checks in the two react hooks eslint rule.  As the api is refined, and the compiler rule(s) are incorporated into this plugin, these checks will be covered then.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 31, 2025
…useEffectEvent

Contributing to facebook#32240, this change removes the experimental `useEffectEvent` checks in the two react hooks eslint rule.  As the api is refined, and the compiler rule(s) are incorporated into this plugin, these checks will be covered then.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 31, 2025
…useEffectEvent

Contributing to facebook#32240, this change removes the experimental `useEffectEvent` checks in the two react hooks eslint rule.  As the api is refined, and the compiler rule(s) are incorporated into this plugin, these checks will be covered then.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 31, 2025
… migration

Contributing to facebook#32240, this change adds the dev dependencies needed to support the migration of the plugin to typescript.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 31, 2025
… migration

Contributing to facebook#32240, this change adds the dev dependencies needed to support the migration of the plugin to typescript.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 31, 2025
… migration

Contributing to facebook#32240, this change adds the dev dependencies needed to support the migration of the plugin to typescript.
poteto pushed a commit that referenced this pull request Jan 31, 2025
… migration (#32279)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Contributing to #32240, this
change adds the dev dependencies needed to support the migration of the
plugin to typescript.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Jan 31, 2025
Contributing to facebook#32240, this change adds the tsconfig, tsup config, and estree type declarations that will be needed for that plugin's typescript migration.
michaelfaith added a commit to michaelfaith/react that referenced this pull request Feb 1, 2025
Contributing to facebook#32240, this change adds the tsconfig, tsup config, and estree type declarations that will be needed for that plugin's typescript migration.
poteto pushed a commit that referenced this pull request Feb 3, 2025
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Contributing to #32240, this
change adds the tsconfig, tsup config, and estree type declarations that
will be needed for that plugin's typescript migration.
@michaelfaith michaelfaith force-pushed the feat/convert-eslint-plugin-hooks-to-typescript branch from 10ad067 to ede1e95 Compare February 3, 2025 23:00
@michaelfaith
Copy link
Contributor Author

I've rebased following the other PRs merging, and resolved merge conflicts. I'll tackle the build changes next. First in this branch, since I have something to work against, and then break it out into a separate change to review on its own.

michaelfaith and others added 9 commits February 16, 2025 12:30
I isolated this mv into its own PR to ensure we're able to maintain commit history for these files.
…pe declarations

This change converts the eslint hooks plugin to typescript, which also allows us to include type declarations in the package, for those using [typescript eslint configs](https://eslint.org/blog/2025/01/eslint-v9.18.0-released/#stable-typescript-configuration-file-support).
…pe declarations

This change converts the eslint hooks plugin to typescript, which also allows us to include type declarations in the package, for those using [typescript eslint configs](https://eslint.org/blog/2025/01/eslint-v9.18.0-released/#stable-typescript-configuration-file-support).
…pe declarations

This change converts the eslint hooks plugin to typescript, which also allows us to include type declarations in the package, for those using [typescript eslint configs](https://eslint.org/blog/2025/01/eslint-v9.18.0-released/#stable-typescript-configuration-file-support).
This change updates the build script to support packages containing typescript source.  I added a new `tsconfig` bundle property to use for detecting typescript js bundle types.  When a bundle has `tsconfig` it will use the `@rollup/typescript` plugin instead of the flow plugin, in the plugin stack.

Additionally, I added two new bundle types for DTS bundles (CJS_DTS and ESM_DTS).  This is for generating .d.ts bundles.  For dts only bundles, we only need to use the dts rollup plugin.  The rest of the bundle creation logic should hold.
Now that eslint-plugin-react-hooks is written in typescript, we need a
pre-step in jest to build the plugin prior to running tests. It's a bit
unfortunate that there are effectively 2 build setups for the eslint
plugin: one from rollup as part of the main React build, and a
standalone tsup (esbuild) step for tests. We should unify these in a
follow up PR.
….ts file

This change adjusts the package.json for the eslint-plugin-react-hooks to point at the product of the root level build, instead of the temporary tsup package-level build.  It also adds an `index.d.ts` to the `npm` folder that's copied to the root of the package after build.  Lastly, by adding a `name` property to the bundle definition, it emits the same file names as it did before changing the `target`.  As a result of these changes, we don't need to do the additional jest global set up.

I'd like to follow this up with a little more setup at the package level to allow for faster local devloop, but that's not critical for this change to land.
This change adds tsup back for package-level testing.  I'll iterate on this to see if we can drop this in a follow-up change
This change removes tsup from the devloop of `eslint-plugin-react-hooks`.  I was originally using it to quickly run tests and builds as I was doing the migration, but now that we've added support to the larger rollup build process, there's no longer a need for it.  I've added an index.js to the root of the package folder that's just a proxy for the `src/index.ts`.  It uses tsx to load it for other consumers in the monorepo (e.g. the eventual e2e tests fixtures).  For jest, the index.js is able to load the ts file due to the added configuration in the package's babel and jest configs.  This `index.js` will only be used for dev purposes.  The `index.js` that's in the `npm` folder is what will actually make it into the published package.
@michaelfaith
Copy link
Contributor Author

Now that we've knocked out and merged all of the prereqs, and I tested everything against the new test fixtures from #32396, this should be ready for review 😊 After this merges I'll update the v9 test fixture to use a ts-based config and add the fixture tests to CI for future validation.

@michaelfaith michaelfaith marked this pull request as ready for review February 16, 2025 18:38
Copy link
Member

@poteto poteto left a comment

Choose a reason for hiding this comment

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

Thanks for all the work getting this ready!


function last(array) {
return array[array.length - 1];
function last<T>(array: T[]): T {
Copy link
Member

Choose a reason for hiding this comment

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

This is super minor but could you use the Array<T> syntax instead for arrays? It's a convention we also use for the compiler, as our team finds them easier to scan.

@@ -140,6 +183,9 @@ export default {

// Get the current scope.
const scope = scopeManager.acquire(node);
if (!scope) {
return;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe throw here if we didn't find a scope?

let declaration = def.node.parent;
if (declaration == null) {
let declaration = defNode.parent;
if (declaration == null && componentScope) {
Copy link
Member

Choose a reason for hiding this comment

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

Could we use a specific comparison here?

@@ -219,6 +266,8 @@ export default {
}
}
if (
declaration &&
Copy link
Member

Choose a reason for hiding this comment

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

here too

@poteto
Copy link
Member

poteto commented Feb 16, 2025

Feel free to address the other comments in a follow up PR, I'm going to merge this for now.

@poteto poteto merged commit 5adf402 into facebook:main Feb 16, 2025
192 checks passed
github-actions bot pushed a commit that referenced this pull request Feb 16, 2025
…pe declarations (#32240)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

This change converts the eslint hooks plugin to typescript, which also
allows us to include type declarations in the package, for those using
[typescript eslint
configs](https://eslint.org/blog/2025/01/eslint-v9.18.0-released/#stable-typescript-configuration-file-support).

### Constituent changes that should land before this one

- [x] ~#32276
- [x] #32279
- [x] #32283
- [x] #32393
- [x] #32396

Closes #30119

---------

Co-authored-by: Lauren Tan <poteto@users.noreply.github.com>

DiffTrain build for [5adf402](5adf402)
@michaelfaith michaelfaith deleted the feat/convert-eslint-plugin-hooks-to-typescript branch February 16, 2025 19:22
michaelfaith added a commit to michaelfaith/react that referenced this pull request Feb 16, 2025
This change addresses several feedback items from facebook#32240
michaelfaith added a commit to michaelfaith/react that referenced this pull request Feb 16, 2025
This change addresses several feedback items from facebook#32240
poteto pushed a commit that referenced this pull request Feb 17, 2025
…nditionals (#32400)

- [build(eslint-plugin-react-hooks): add
ts-linting](4c0fbe7)
This change adds configuration to the eslint config governing
`eslint-plugin-react-hooks` to use the typescript-eslint plugin and
parser. It adds the typescript-recommended config, and configures the
team's preferred `array-type` convention.

- [refactor(eslint-plugin-react-hooks): improve
conditionals](540d0d9)
This change addresses several feedback items from
#32240

- [ci (eslint-e2e): exclude nested node_modules from
cache](a3279f4)
This change removes the nested fixture `node_modules` from being cached,
so that the symbolic link can be made after the build happens.
github-actions bot pushed a commit that referenced this pull request Feb 17, 2025
…nditionals (#32400)

- [build(eslint-plugin-react-hooks): add
ts-linting](4c0fbe7)
This change adds configuration to the eslint config governing
`eslint-plugin-react-hooks` to use the typescript-eslint plugin and
parser. It adds the typescript-recommended config, and configures the
team's preferred `array-type` convention.

- [refactor(eslint-plugin-react-hooks): improve
conditionals](540d0d9)
This change addresses several feedback items from
#32240

- [ci (eslint-e2e): exclude nested node_modules from
cache](a3279f4)
This change removes the nested fixture `node_modules` from being cached,
so that the symbolic link can be made after the build happens.

DiffTrain build for [4632e36](4632e36)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[eslint-plugin-react-hooks] Missing type declarations
6 participants