Skip to content

Commit

Permalink
feat: Version 4 codemods (#3195)
Browse files Browse the repository at this point in the history
* chore: add `jscodeshift` library to dev-dependencies

We're planning to deliver codemods for the next release, so we will
need this package to write codemods.

* chore: add `@types/jscodeshift` library to dev-dependencies

* chore: add an empty `jest` config file under the `codemods` directory

For some reason the `moduleNameMapper` causes an error when running the
codemod tests, it results in a `transformer is a not function` type
error. In order to avoid this now, I add an empty config file and will
investigate the root cause of the issue later.

* chore: add a very basic codemod implementation and tests

The current codemod is able to change the usages of `useQuery`
according to the new API, so it will put the existing parameters into
an object and pass it as the only parameter, but that's all. More
changes coming soon.

* chore: fix `prettier` errors in `use_query` codemod

* chore: add missing `export` keywords to test suites in case of `use_query` codemod

I just want to avoid `eslint` errors in the IDE.

* feat(codemod): add basic codemods for query client methods

* feat(codemod): add basic `useQueries` codemods

* refactor(codemod): rework `useQuery` related codemods

* feat(codemod): move files under `v4` directory

We decided to move the version 4 related codemods under a separate
directory.

* chore(codemod): move `jscodeshift` library from `dependencies` to `devDependencies`

Accidentally I added this package under `dependencies` but it should be
placed under `devDependencies`.

* chore(codemod): pick up codemods test by `npm test`

From now the `npm test` command will execute the codemod tests as well.

* refactor(codemod): remove `without-parameter` test case in case of `queryClient` related codemods

This test case is not useful at all, it just adds extra code.

* chore(codemod): remove duplications from `queryClient` codemod testfixtures

The number of duplications was just simply too much. It doesn't make
any sense to maintain repetitive boilerplate code.

* chore(codemod): rename `first-parameter-is-identifier` and `object-expression-parameter` test case

* chore(codemod): rework `first-parameter-is-identifier` and `object-expression-parameter` test suites

In these two test cases, we should try to collect as many test cases
as possible.

* chore(codemod): remove duplications from `useQuery` codemod testfixtures

The number of duplications was just simply too much. It doesn't make
any sense to maintain repetitive boilerplate code.

* chore(codemod): add missing methods to the object syntax aware query client codemods

The following methods were missing:
- fetchInfiniteQuery
- fetchQuery
- prefetchInfiniteQuery
- prefetchQuery

* chore(codemod): add some more test cases to the `parameter-is-object` test suite

* feat(codemod): add basic `useMutation` codemods

* refactor(codemod): rename `queryKey` replacer and error, because now the key name can be parameterized

* feat(codemod): add support for template literals

* refactor(codemod): rename `object-syntax-aware` test to `query-client-methods`

I want to combine two tests into a single one.

* refactor(codemod): move testfixtures to the root level of `__testfixtures__` directory

* refactor(codemod): combine the two `queryClient` codemods and test suites into a single one

* refactor(codemod): move `useMutation` related parts and testfixutes to `useQuery` codemod

The plan is to combine all existing codemods into a single one.

* refactor(codemod): remove the whole `use_mutation` library

Regarding the necessary parts that were moved to the `useQuery`
codemod, we don't need the leftover code anymore.

* feat(codemod): add support for the following hook calls:

- `useIsFetching`
- `useIsMutating`

* refactor(codemod): move `useQueries` related parts and testfixutes to `useQuery` codemod

The plan is to combine all existing codemods into a single one.

* refactor(codemod): remove the whole `use_queries` library

Regarding the necessary parts that were moved to the `useQuery`
codemod, we don't need the leftover code anymore.

* refactor(codemod): introduce `transformUseQueryLikeUsages` function

Maybe this change will make the code read easier a bit.

* refactor(codemod): do some refinements on the following tests:

- `parameter-is-identifier`
- `parameter-is-object-expression`

* fix(codemod): in the case of JS, the string literal might be interpreted as `Literal`

So without the additional `Literal` check, the codemod wouldn't be
applied.

* fix(codemod): remove `type` filter from import declarations

This filter seems to be too strict because the codemods were not
applied to the `examples` directory.

* refactor(codemod): move `QueryClient` related parts and testfixutes to `useQuery` codemod

The plan is to combine all existing codemods into a single one.

* refactor(codemod): remove the whole `query_client_methods` library

Regarding the necessary parts that were moved to the `useQuery`
codemod, we don't need the leftover code anymore.

* refactor(codemod): simplify entry point of the codemod

It makes the code a bit slower, but on the other hand, it makes the
code easier to read and understand.

* feat(codemod): add basic `QueryCache` codemods

* fix(codemod): do not transform array expression query keys

We want arrays as query keys, so when the current query key is an
array, we don't need to transform it.

* fix(examples/playground): fix invalid usage of `invalidateQueries`

* feat(codemod): show file path in console warnings

* refactor(codemod): remove `use_query` directory

We decided to group the codemods by major version numbers instead of
types. The reason is simple: we will bundle all necessary
transformations into a single file, so the consumers will have to
apply only one codemod. Hopefully, it will make the DX better.

* refactor(codemod): rename `use-query` to `key-transformation`

The original `use-query` name is not valid anymore, because the
codemod changes the signature of other function/method calls as well.

* refactor(codemod): replace the transformer name in `key-transformation` codemod

I renamed the transformer with the test, so the transformer name also
needs to be updated in the test file.

* chore(codemod): add codemod and utilities to the bundle

We want the code mod to be part of the bundle. If it's part of the
bundle, the consumers can directly access it through the `node_modules`
directory and run it. We just simply would like to increase the DX.

* refactor(codemod): rename function

I shouldn't have committed this. :')

* fix(codemod): keep `typeArguments` of the node

It will prevent the removal of type annotations on a function/method
call.

* refactor(codemod): move `query-cache` test cases to `default-import`

We want to cover the default, named, and namespaced imports in the
case of `QueryCache` as well.

* feat(codemod): add support for named imports in case of `QueryCache` and `useQueryCache`

The codemod must be able to transform usages in the case of named
imports as well.

* fix(codemod): always return `Identifier` instance in case of `findImportIdentifier` of function

Previously it returned a string as a default value and it could lead
to errors in the code.

* feat(codemod): add support for namespaced imports in case of `QueryCache` and `useQueryCache`

The codemod must be able to transform usages in the case of namespaced
imports as well.

* refactor(codemod): re-use the previously written utility functions in the hook call transformer

By this, we can achieve the same functionality with less code
repetition.

* refactor(codemod): re-use the previously written utility functions in the query-client transformer

By this, we can achieve the same functionality with less code
repetition.

* refactor(codemod): rename `hook-call-transformer` to `use-query-like` transformer

I think this name describes better the purpose of this transformer.

* chore(codemod): add todo about the `react-query` import check

We shouldn't transform files that don't contain `react-query` imports.

* refactor(codemod): make the `use-query-like-transformer` to look the same as other transformers

I just want to follow the structure of other transformers.

* fix(codemod): do not log warnings in the test environment

Warning the user about the unprocessable query keys is useful in
production, but in the test environment, it just makes it more
difficult to read the output.

* docs(codemod): add instructions how to apply codemods

* chore(examples): apply codemod on the `examples` directory

* docs(codemod): update `Codemod` section

The suggested text sounds better. :)

Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>

Co-authored-by: Dominik Dorfmeister <office@dorfmeister.cc>
  • Loading branch information
balazsmatepetro and TkDodo committed Feb 18, 2022
1 parent 293e5e9 commit ada0e34
Show file tree
Hide file tree
Showing 45 changed files with 2,296 additions and 61 deletions.
3 changes: 3 additions & 0 deletions codemods/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
testMatch: ['<rootDir>/**/*.test.js'],
}
94 changes: 94 additions & 0 deletions codemods/v4/__testfixtures__/default-import.input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import * as React from 'react'
import {
QueryCache,
QueryClient,
useInfiniteQuery,
useIsFetching,
useIsMutating,
useMutation,
useQueries,
useQuery,
useQueryClient,
} from 'react-query'

export const Examples = () => {
useQuery('todos')
useInfiniteQuery('todos')
useMutation('todos')
useIsFetching('todos')
useIsMutating('todos')
useQueries([query1, query2])
// QueryClient methods
// --- Instantiated hook call.
const queryClient = useQueryClient()
queryClient.getMutationDefaults('todos')
queryClient.getQueriesData('todos')
queryClient.getQueryData('todos')
queryClient.getQueryDefaults('todos')
queryClient.getQueryState('todos')
queryClient.isFetching('todos')
queryClient.setMutationDefaults('todos', { mutationFn: async () => null })
queryClient.setQueriesData('todos', () => null)
queryClient.setQueryData('todos', () => null)
queryClient.setQueryDefaults('todos', { queryFn: async () => null })
queryClient.cancelQueries('todos')
queryClient.fetchInfiniteQuery('todos')
queryClient.fetchQuery('todos')
queryClient.invalidateQueries('todos')
queryClient.prefetchInfiniteQuery('todos')
queryClient.prefetchQuery('todos')
queryClient.refetchQueries('todos')
queryClient.removeQueries('todos')
queryClient.resetQueries('todos')
// --- Direct hook call.
useQueryClient().getMutationDefaults('todos')
useQueryClient().getQueriesData('todos')
useQueryClient().getQueryData('todos')
useQueryClient().getQueryDefaults('todos')
useQueryClient().getQueryState('todos')
useQueryClient().isFetching('todos')
useQueryClient().setMutationDefaults('todos', {
mutationFn: async () => null,
})
useQueryClient().setQueriesData('todos', () => null)
useQueryClient().setQueryData('todos', () => null)
useQueryClient().setQueryDefaults('todos', { queryFn: async () => null })
useQueryClient().cancelQueries('todos')
useQueryClient().fetchInfiniteQuery('todos')
useQueryClient().fetchQuery('todos')
useQueryClient().invalidateQueries('todos')
useQueryClient().prefetchInfiniteQuery('todos')
useQueryClient().prefetchQuery('todos')
useQueryClient().refetchQueries('todos')
useQueryClient().removeQueries('todos')
useQueryClient().resetQueries('todos')
// QueryCache
// --- NewExpression
const queryCache1 = new QueryCache({
onError: (error) => console.log(error),
onSuccess: (success) => console.log(success)
})
queryCache1.find('todos')
queryCache1.findAll('todos')
// --- Instantiated hook call.
const queryClient1 = useQueryClient()
queryClient1.getQueryCache().find('todos')
queryClient1.getQueryCache().findAll('todos')
//
const queryClient2 = new QueryClient({})
queryClient2.getQueryCache().find('todos')
queryClient2.getQueryCache().findAll('todos')
//
const queryCache2 = queryClient1.getQueryCache()
queryCache2.find('todos')
queryCache2.findAll('todos')
// --- Direct hook call.
useQueryClient().getQueryCache().find('todos')
useQueryClient().getQueryCache().findAll('todos')
//
const queryCache3 = useQueryClient().getQueryCache()
queryCache3.find('todos')
queryCache3.findAll('todos')

return <div>Example Component</div>
}
96 changes: 96 additions & 0 deletions codemods/v4/__testfixtures__/default-import.output.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as React from 'react'
import {
QueryCache,
QueryClient,
useInfiniteQuery,
useIsFetching,
useIsMutating,
useMutation,
useQueries,
useQuery,
useQueryClient,
} from 'react-query'

export const Examples = () => {
useQuery(['todos'])
useInfiniteQuery(['todos'])
useMutation(['todos'])
useIsFetching(['todos'])
useIsMutating(['todos'])
useQueries({
queries: [query1, query2]
})
// QueryClient methods
// --- Instantiated hook call.
const queryClient = useQueryClient()
queryClient.getMutationDefaults(['todos'])
queryClient.getQueriesData(['todos'])
queryClient.getQueryData(['todos'])
queryClient.getQueryDefaults(['todos'])
queryClient.getQueryState(['todos'])
queryClient.isFetching(['todos'])
queryClient.setMutationDefaults(['todos'], { mutationFn: async () => null })
queryClient.setQueriesData(['todos'], () => null)
queryClient.setQueryData(['todos'], () => null)
queryClient.setQueryDefaults(['todos'], { queryFn: async () => null })
queryClient.cancelQueries(['todos'])
queryClient.fetchInfiniteQuery(['todos'])
queryClient.fetchQuery(['todos'])
queryClient.invalidateQueries(['todos'])
queryClient.prefetchInfiniteQuery(['todos'])
queryClient.prefetchQuery(['todos'])
queryClient.refetchQueries(['todos'])
queryClient.removeQueries(['todos'])
queryClient.resetQueries(['todos'])
// --- Direct hook call.
useQueryClient().getMutationDefaults(['todos'])
useQueryClient().getQueriesData(['todos'])
useQueryClient().getQueryData(['todos'])
useQueryClient().getQueryDefaults(['todos'])
useQueryClient().getQueryState(['todos'])
useQueryClient().isFetching(['todos'])
useQueryClient().setMutationDefaults(['todos'], {
mutationFn: async () => null,
})
useQueryClient().setQueriesData(['todos'], () => null)
useQueryClient().setQueryData(['todos'], () => null)
useQueryClient().setQueryDefaults(['todos'], { queryFn: async () => null })
useQueryClient().cancelQueries(['todos'])
useQueryClient().fetchInfiniteQuery(['todos'])
useQueryClient().fetchQuery(['todos'])
useQueryClient().invalidateQueries(['todos'])
useQueryClient().prefetchInfiniteQuery(['todos'])
useQueryClient().prefetchQuery(['todos'])
useQueryClient().refetchQueries(['todos'])
useQueryClient().removeQueries(['todos'])
useQueryClient().resetQueries(['todos'])
// QueryCache
// --- NewExpression
const queryCache1 = new QueryCache({
onError: (error) => console.log(error),
onSuccess: (success) => console.log(success)
})
queryCache1.find(['todos'])
queryCache1.findAll(['todos'])
// --- Instantiated hook call.
const queryClient1 = useQueryClient()
queryClient1.getQueryCache().find(['todos'])
queryClient1.getQueryCache().findAll(['todos'])
//
const queryClient2 = new QueryClient({})
queryClient2.getQueryCache().find(['todos'])
queryClient2.getQueryCache().findAll(['todos'])
//
const queryCache2 = queryClient1.getQueryCache()
queryCache2.find(['todos'])
queryCache2.findAll(['todos'])
// --- Direct hook call.
useQueryClient().getQueryCache().find(['todos'])
useQueryClient().getQueryCache().findAll(['todos'])
//
const queryCache3 = useQueryClient().getQueryCache()
queryCache3.find(['todos'])
queryCache3.findAll(['todos'])

return <div>Example Component</div>
}
96 changes: 96 additions & 0 deletions codemods/v4/__testfixtures__/named-import.input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import * as React from 'react'
import {
QueryCache as RenamedQueryCache,
QueryClient as RenamedQueryClient,
useInfiniteQuery as useRenamedInfiniteQuery,
useIsFetching as useRenamedIsFetching,
useIsMutating as useRenamedIsMutating,
useMutation as useRenamedMutation,
useQueries as useRenamedQueries,
useQuery as useRenamedQuery,
useQueryClient as useRenamedQueryClient,
} from 'react-query'

export const Examples = () => {
useRenamedQuery('todos')
useRenamedInfiniteQuery('todos')
useRenamedMutation('todos')
useRenamedIsFetching('todos')
useRenamedIsMutating('todos')
useRenamedQueries([query1, query2])
// QueryClient methods
// --- Instantiated hook call.
const queryClient = useRenamedQueryClient()
queryClient.getMutationDefaults('todos')
queryClient.getQueriesData('todos')
queryClient.getQueryData('todos')
queryClient.getQueryDefaults('todos')
queryClient.getQueryState('todos')
queryClient.isFetching('todos')
queryClient.setMutationDefaults('todos', { mutationFn: async () => null })
queryClient.setQueriesData('todos', () => null)
queryClient.setQueryData('todos', () => null)
queryClient.setQueryDefaults('todos', { queryFn: async () => null })
queryClient.cancelQueries('todos')
queryClient.fetchInfiniteQuery('todos')
queryClient.fetchQuery('todos')
queryClient.invalidateQueries('todos')
queryClient.prefetchInfiniteQuery('todos')
queryClient.prefetchQuery('todos')
queryClient.refetchQueries('todos')
queryClient.removeQueries('todos')
queryClient.resetQueries('todos')
// --- Direct hook call.
useRenamedQueryClient().getMutationDefaults('todos')
useRenamedQueryClient().getQueriesData('todos')
useRenamedQueryClient().getQueryData('todos')
useRenamedQueryClient().getQueryDefaults('todos')
useRenamedQueryClient().getQueryState('todos')
useRenamedQueryClient().isFetching('todos')
useRenamedQueryClient().setMutationDefaults('todos', {
mutationFn: async () => null,
})
useRenamedQueryClient().setQueriesData('todos', () => null)
useRenamedQueryClient().setQueryData('todos', () => null)
useRenamedQueryClient().setQueryDefaults('todos', {
queryFn: async () => null,
})
useRenamedQueryClient().cancelQueries('todos')
useRenamedQueryClient().fetchInfiniteQuery('todos')
useRenamedQueryClient().fetchQuery('todos')
useRenamedQueryClient().invalidateQueries('todos')
useRenamedQueryClient().prefetchInfiniteQuery('todos')
useRenamedQueryClient().prefetchQuery('todos')
useRenamedQueryClient().refetchQueries('todos')
useRenamedQueryClient().removeQueries('todos')
useRenamedQueryClient().resetQueries('todos')
// QueryCache
// --- NewExpression
const queryCache1 = new RenamedQueryCache({
onError: (error) => console.log(error),
onSuccess: (success) => console.log(success)
})
queryCache1.find('todos')
queryCache1.findAll('todos')
// --- Instantiated hook call.
const queryClient1 = useRenamedQueryClient()
queryClient1.getQueryCache().find('todos')
queryClient1.getQueryCache().findAll('todos')
//
const queryClient2 = new RenamedQueryClient({})
queryClient2.getQueryCache().find('todos')
queryClient2.getQueryCache().findAll('todos')
//
const queryCache2 = queryClient1.getQueryCache()
queryCache2.find('todos')
queryCache2.findAll('todos')
// --- Direct hook call.
useRenamedQueryClient().getQueryCache().find('todos')
useRenamedQueryClient().getQueryCache().findAll('todos')
//
const queryCache3 = useRenamedQueryClient().getQueryCache()
queryCache3.find('todos')
queryCache3.findAll('todos')

return <div>Example Component</div>
}
98 changes: 98 additions & 0 deletions codemods/v4/__testfixtures__/named-import.output.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import * as React from 'react'
import {
QueryCache as RenamedQueryCache,
QueryClient as RenamedQueryClient,
useInfiniteQuery as useRenamedInfiniteQuery,
useIsFetching as useRenamedIsFetching,
useIsMutating as useRenamedIsMutating,
useMutation as useRenamedMutation,
useQueries as useRenamedQueries,
useQuery as useRenamedQuery,
useQueryClient as useRenamedQueryClient,
} from 'react-query'

export const Examples = () => {
useRenamedQuery(['todos'])
useRenamedInfiniteQuery(['todos'])
useRenamedMutation(['todos'])
useRenamedIsFetching(['todos'])
useRenamedIsMutating(['todos'])
useRenamedQueries({
queries: [query1, query2]
})
// QueryClient methods
// --- Instantiated hook call.
const queryClient = useRenamedQueryClient()
queryClient.getMutationDefaults(['todos'])
queryClient.getQueriesData(['todos'])
queryClient.getQueryData(['todos'])
queryClient.getQueryDefaults(['todos'])
queryClient.getQueryState(['todos'])
queryClient.isFetching(['todos'])
queryClient.setMutationDefaults(['todos'], { mutationFn: async () => null })
queryClient.setQueriesData(['todos'], () => null)
queryClient.setQueryData(['todos'], () => null)
queryClient.setQueryDefaults(['todos'], { queryFn: async () => null })
queryClient.cancelQueries(['todos'])
queryClient.fetchInfiniteQuery(['todos'])
queryClient.fetchQuery(['todos'])
queryClient.invalidateQueries(['todos'])
queryClient.prefetchInfiniteQuery(['todos'])
queryClient.prefetchQuery(['todos'])
queryClient.refetchQueries(['todos'])
queryClient.removeQueries(['todos'])
queryClient.resetQueries(['todos'])
// --- Direct hook call.
useRenamedQueryClient().getMutationDefaults(['todos'])
useRenamedQueryClient().getQueriesData(['todos'])
useRenamedQueryClient().getQueryData(['todos'])
useRenamedQueryClient().getQueryDefaults(['todos'])
useRenamedQueryClient().getQueryState(['todos'])
useRenamedQueryClient().isFetching(['todos'])
useRenamedQueryClient().setMutationDefaults(['todos'], {
mutationFn: async () => null,
})
useRenamedQueryClient().setQueriesData(['todos'], () => null)
useRenamedQueryClient().setQueryData(['todos'], () => null)
useRenamedQueryClient().setQueryDefaults(['todos'], {
queryFn: async () => null,
})
useRenamedQueryClient().cancelQueries(['todos'])
useRenamedQueryClient().fetchInfiniteQuery(['todos'])
useRenamedQueryClient().fetchQuery(['todos'])
useRenamedQueryClient().invalidateQueries(['todos'])
useRenamedQueryClient().prefetchInfiniteQuery(['todos'])
useRenamedQueryClient().prefetchQuery(['todos'])
useRenamedQueryClient().refetchQueries(['todos'])
useRenamedQueryClient().removeQueries(['todos'])
useRenamedQueryClient().resetQueries(['todos'])
// QueryCache
// --- NewExpression
const queryCache1 = new RenamedQueryCache({
onError: (error) => console.log(error),
onSuccess: (success) => console.log(success)
})
queryCache1.find(['todos'])
queryCache1.findAll(['todos'])
// --- Instantiated hook call.
const queryClient1 = useRenamedQueryClient()
queryClient1.getQueryCache().find(['todos'])
queryClient1.getQueryCache().findAll(['todos'])
//
const queryClient2 = new RenamedQueryClient({})
queryClient2.getQueryCache().find(['todos'])
queryClient2.getQueryCache().findAll(['todos'])
//
const queryCache2 = queryClient1.getQueryCache()
queryCache2.find(['todos'])
queryCache2.findAll(['todos'])
// --- Direct hook call.
useRenamedQueryClient().getQueryCache().find(['todos'])
useRenamedQueryClient().getQueryCache().findAll(['todos'])
//
const queryCache3 = useRenamedQueryClient().getQueryCache()
queryCache3.find(['todos'])
queryCache3.findAll(['todos'])

return <div>Example Component</div>
}

0 comments on commit ada0e34

Please sign in to comment.